@leancodepl/folder-structure-cruiser 10.3.1 → 10.4.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.
- package/CHANGELOG.md +12 -0
- package/README.md +21 -0
- package/dist/bin.cjs +1 -1
- package/dist/bin.js +1 -1
- package/dist/commands/validateCrossFeatureImports.d.ts +5 -4
- package/dist/commands/validateCrossFeatureImports.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/checkCrossFeatureImports.d.ts.map +1 -1
- package/dist/validateSharedComponent-BHinLxNm.cjs +3 -0
- package/dist/validateSharedComponent-D24GPyRZ.js +125 -0
- package/package.json +2 -2
- package/dist/validateSharedComponent-CZqzEHqN.js +0 -113
- package/dist/validateSharedComponent-oM_e4L8S.cjs +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See
|
|
4
4
|
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [10.4.0](https://github.com/leancodepl/js_corelibrary/compare/v10.3.1...v10.4.0) (2026-06-23)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- skip opaque segments and external dependencies in cross-feature-imports check
|
|
11
|
+
([b2cb6dd](https://github.com/leancodepl/js_corelibrary/commit/b2cb6ddf79345444c5dd20bdaadcb8901c99e510))
|
|
12
|
+
|
|
13
|
+
# Change Log
|
|
14
|
+
|
|
15
|
+
All notable changes to this project will be documented in this file. See
|
|
16
|
+
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
17
|
+
|
|
6
18
|
## [10.3.1](https://github.com/leancodepl/js_corelibrary/compare/v10.3.0...v10.3.1) (2026-05-20)
|
|
7
19
|
|
|
8
20
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -97,6 +97,10 @@ Imports are allowed only if they meet one of these conditions:
|
|
|
97
97
|
2. **Sibling's immediate child**: Import is from an immediate sibling's or ancestors siblings' child
|
|
98
98
|
- ✅ `src/feature1/subfeature1/ComponentA` → `src/feature1/subfeature2/ComponentB`
|
|
99
99
|
|
|
100
|
+
3. **Through an opaque directory**: any directory whose name ends with an underscore is [opaque](#opaque-directories)
|
|
101
|
+
and doesn't count toward the nesting depth
|
|
102
|
+
- ✅ `src/feature1/ComponentA` → `src/features_/feature2/ComponentB`
|
|
103
|
+
|
|
100
104
|
### Shared Component Detection
|
|
101
105
|
|
|
102
106
|
Identifies components that should be moved to shared levels when:
|
|
@@ -116,6 +120,23 @@ Create a `.dependency-cruiser.json` file in your project root:
|
|
|
116
120
|
|
|
117
121
|
This configuration serves as a base config that can be extended with your own rules.
|
|
118
122
|
|
|
123
|
+
### Opaque directories
|
|
124
|
+
|
|
125
|
+
Some directories only group features together (e.g. a `features/` folder that holds every feature) rather than being a
|
|
126
|
+
feature themselves. Counting them toward the nesting depth would make every cross-feature import look one level too
|
|
127
|
+
deep. Mark such a directory as **opaque** by ending its name with an underscore — `features_`, `modules_`, etc. Opaque
|
|
128
|
+
segments are skipped when measuring how deep an import reaches, so the directory behaves as if it weren't there.
|
|
129
|
+
|
|
130
|
+
This needs no configuration; it's driven purely by the directory name. With a `src/features_/` container:
|
|
131
|
+
|
|
132
|
+
- ✅ `src/features_/featureA/X` → `src/features_/featureB/Y` (`features_` is skipped, so these read as sibling features)
|
|
133
|
+
- ✅ `src/featureA/X` → `src/features_/featureB/Y` (`featureB` is a top-level feature once `features_` is skipped)
|
|
134
|
+
- ❌ `src/featureA/X` → `src/features_/featureB/internal/Y` (`internal/Y` is still a grandchild of `featureB`, one level
|
|
135
|
+
too deep)
|
|
136
|
+
|
|
137
|
+
The trailing-underscore convention is matched verbatim, so a sibling directory like `features` (no underscore) keeps
|
|
138
|
+
counting normally.
|
|
139
|
+
|
|
119
140
|
## Nx Configuration
|
|
120
141
|
|
|
121
142
|
Configure folder-structure-cruiser commands as Nx target in your `project.json`. Example configuration:
|
package/dist/bin.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";const t=require("commander"),r=require("./validateSharedComponent-
|
|
2
|
+
"use strict";const t=require("commander"),r=require("./validateSharedComponent-BHinLxNm.cjs");t.program.name("folder-structure-cruiser").description("CLI tool for validating folder structure rules");function s(o){o>0&&(process.exitCode=1)}function f(o){r.logger.error(o instanceof Error?o:new Error(String(o))),process.exitCode=1}t.program.command("validate-shared-components").description("Validate if shared components are located at the first shared level").option("-d, --directory <dir>","Directory to analyze",".").option("-c, --config <path_to_config>","Path to config file").option("-t, --tsConfig <path_to_ts_config>","Path to ts config file").option("-w, --webpackConfig <path_to_webpack_config>","Path to webpack config file").action(async o=>{const e=o.directory?[o.directory]:[".*"],i=o.config??"",a=o.tsConfig,n=o.webpackConfig,c=await r.validateSharedComponent({directories:e,configPath:i,tsConfigPath:a,webpackConfigPath:n});s(c)});t.program.command("validate-cross-feature-imports").description("Validate if cross-feature nested imports are allowed").option("-d, --directory <dir>","Directory to analyze",".").option("-c, --config <path_to_config>","Path to config file").option("-t, --tsConfig <path_to_ts_config>","Path to ts config file").option("-w, --webpackConfig <path_to_webpack_config>","Path to webpack config file").action(async o=>{const e=o.directory?[o.directory]:[".*"],i=o.config??"",a=o.tsConfig,n=o.webpackConfig,c=await r.validateCrossFeatureImports({directories:e,configPath:i,tsConfigPath:a,webpackConfigPath:n});s(c)});t.program.parseAsync().catch(f);
|
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { program as t } from "commander";
|
|
3
|
-
import { a as s, v as f, l as d } from "./validateSharedComponent-
|
|
3
|
+
import { a as s, v as f, l as d } from "./validateSharedComponent-D24GPyRZ.js";
|
|
4
4
|
t.name("folder-structure-cruiser").description("CLI tool for validating folder structure rules");
|
|
5
5
|
function r(o) {
|
|
6
6
|
o > 0 && (process.exitCode = 1);
|
|
@@ -2,10 +2,11 @@ import { CruiseParams } from '../lib/getCruiseResult.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Validates cross-feature nested imports according to folder structure rules.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Analyzes the module graph of the given directories and reports imports that
|
|
6
|
+
* reach into another feature deeper than its immediate children. Directories
|
|
7
|
+
* whose name ends with an underscore (e.g. `features_`) are opaque and don't
|
|
8
|
+
* count toward the nesting depth. Imports of Node built-ins and npm packages
|
|
9
|
+
* are left out of the analysis.
|
|
9
10
|
*
|
|
10
11
|
* The function will output violations to the console, showing which modules have
|
|
11
12
|
* cross-feature import issues that need to be resolved.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateCrossFeatureImports.d.ts","sourceRoot":"","sources":["../../src/commands/validateCrossFeatureImports.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAmB,MAAM,2BAA2B,CAAA;AAGzE
|
|
1
|
+
{"version":3,"file":"validateCrossFeatureImports.d.ts","sourceRoot":"","sources":["../../src/commands/validateCrossFeatureImports.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAmB,MAAM,2BAA2B,CAAA;AAGzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,wBAAsB,2BAA2B,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB7F"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./validateSharedComponent-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./validateSharedComponent-BHinLxNm.cjs");exports.validateCrossFeatureImports=e.validateCrossFeatureImports;exports.validateSharedComponent=e.validateSharedComponent;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkCrossFeatureImports.d.ts","sourceRoot":"","sources":["../../src/lib/checkCrossFeatureImports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,KAAK,WAAW,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"checkCrossFeatureImports.d.ts","sourceRoot":"","sources":["../../src/lib/checkCrossFeatureImports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,KAAK,WAAW,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CA0C7E"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";const x=require("chalk"),P=require("dependency-cruiser"),y=require("dependency-cruiser/config-utl/extract-depcruise-options"),$=require("dependency-cruiser/config-utl/extract-ts-config"),q=require("dependency-cruiser/config-utl/extract-webpack-resolve-config"),F=require("@leancodepl/logger/cli");function g(e){if(e.length===0)return[];if(e.length===1)return e[0];const t=[],n=Math.min(...e.map(s=>s.length));for(let s=0;s<n;s++){const o=e[0][s];if(e.every(r=>r[s]===o))t.push(o);else break}return t}function p(e){return g(e).length}function M(e){const t=typeof e.output=="object"?e.output:void 0,n=t?.modules??[],s=[];for(const o of n){if(o.coreModule)continue;const r=o.dependencies||[],i=f(o.source.split("/"));r.forEach(u=>{if(k(u.dependencyTypes))return;const l=f(u.resolved.split("/")),c=p([i,l]);(!c||c<i.length&&l.length>c+j)&&s.push({source:o.source,target:u.resolved,rule:"cross-feature-nested-imports",severity:"error"})})}return{messages:s,totalCruised:t?.summary.totalCruised??0}}const j=2;function k(e){return e?.some(t=>t==="core"||t==="unknown"||t.startsWith("npm"))??!1}function w(e){return/[^_]_$/.test(e)}function f(e){return e.filter(t=>!w(t))}const{bold:m}=x;function h(e){return e.map(t=>`${t.rule}: ${m(t.source)} → ${m(t.target)}`)}async function C({directories:e=[".*"],configPath:t,tsConfigPath:n,webpackConfigPath:s}){const o=await y(t),r=s?await q(s):void 0,i=n?$(n):void 0;return await P.cruise(e,{...o},r,{tsConfig:i})}const a=F.createCliLogger();async function L(e){const t=await C(e),{messages:n,totalCruised:s}=M(t);if(n.length===0&&a.success("✅ No issues found!"),n.length>0){const o=h(n);a.error(o.join(`
|
|
2
|
+
`)),a.error(`Found ${n.length} violation(s). ${s} modules cruised.`)}return n.length}function R(e,t,n){const s=e.at(-1)??"";if(!/index\.(tsx?|jsx?|ts|js)$/.test(s))return!1;const o=e.at(-3)??"",r=t.at(-1);return o===r&&n===e.length-2}function S(e){const t=typeof e.output=="object"?e.output:void 0,n=t?.modules??[],s=[];for(const o of n){if(o.coreModule)continue;const r=o.dependents||[];if(r.length<=1)continue;const i=o.source.split("/"),u=i.length;if(u<=2)continue;const l=r.map(v=>v.split("/")),c=g(l),d=p([i,c]);R(i,c,d)||d<u-1&&s.push({source:o.source,target:c.join("/"),rule:"not-shared-level",severity:"info"})}return{messages:s,totalCruised:t?.summary.totalCruised??0}}async function b(e){const t=await C(e),{messages:n,totalCruised:s}=S(t);if(n.length===0&&a.success("✅ No issues found!"),n.length>0){const o=h(n);a.info(o.join(`
|
|
3
|
+
`)),a.info(`Found ${n.length} violation(s). ${s} modules cruised.`)}return n.length}exports.logger=a;exports.validateCrossFeatureImports=L;exports.validateSharedComponent=b;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import C from "chalk";
|
|
2
|
+
import { cruise as P } from "dependency-cruiser";
|
|
3
|
+
import y from "dependency-cruiser/config-utl/extract-depcruise-options";
|
|
4
|
+
import $ from "dependency-cruiser/config-utl/extract-ts-config";
|
|
5
|
+
import M from "dependency-cruiser/config-utl/extract-webpack-resolve-config";
|
|
6
|
+
import { createCliLogger as j } from "@leancodepl/logger/cli";
|
|
7
|
+
function p(e) {
|
|
8
|
+
if (e.length === 0) return [];
|
|
9
|
+
if (e.length === 1) return e[0];
|
|
10
|
+
const t = [], o = Math.min(...e.map((n) => n.length));
|
|
11
|
+
for (let n = 0; n < o; n++) {
|
|
12
|
+
const s = e[0][n];
|
|
13
|
+
if (e.every((r) => r[n] === s))
|
|
14
|
+
t.push(s);
|
|
15
|
+
else
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
return t;
|
|
19
|
+
}
|
|
20
|
+
function g(e) {
|
|
21
|
+
return p(e).length;
|
|
22
|
+
}
|
|
23
|
+
function F(e) {
|
|
24
|
+
const t = typeof e.output == "object" ? e.output : void 0, o = t?.modules ?? [], n = [];
|
|
25
|
+
for (const s of o) {
|
|
26
|
+
if (s.coreModule)
|
|
27
|
+
continue;
|
|
28
|
+
const r = s.dependencies || [], i = m(s.source.split("/"));
|
|
29
|
+
r.forEach((u) => {
|
|
30
|
+
if (w(u.dependencyTypes))
|
|
31
|
+
return;
|
|
32
|
+
const l = m(u.resolved.split("/")), c = g([i, l]);
|
|
33
|
+
(!c || c < i.length && l.length > c + k) && n.push({
|
|
34
|
+
source: s.source,
|
|
35
|
+
target: u.resolved,
|
|
36
|
+
rule: "cross-feature-nested-imports",
|
|
37
|
+
severity: "error"
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
messages: n,
|
|
43
|
+
totalCruised: t?.summary.totalCruised ?? 0
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const k = 2;
|
|
47
|
+
function w(e) {
|
|
48
|
+
return e?.some((t) => t === "core" || t === "unknown" || t.startsWith("npm")) ?? !1;
|
|
49
|
+
}
|
|
50
|
+
function L(e) {
|
|
51
|
+
return /[^_]_$/.test(e);
|
|
52
|
+
}
|
|
53
|
+
function m(e) {
|
|
54
|
+
return e.filter((t) => !L(t));
|
|
55
|
+
}
|
|
56
|
+
const { bold: d } = C;
|
|
57
|
+
function h(e) {
|
|
58
|
+
return e.map((t) => `${t.rule}: ${d(t.source)} → ${d(t.target)}`);
|
|
59
|
+
}
|
|
60
|
+
async function x({
|
|
61
|
+
directories: e = [".*"],
|
|
62
|
+
configPath: t,
|
|
63
|
+
tsConfigPath: o,
|
|
64
|
+
webpackConfigPath: n
|
|
65
|
+
}) {
|
|
66
|
+
const s = await y(t), r = n ? await M(n) : void 0, i = o ? $(o) : void 0;
|
|
67
|
+
return await P(e, { ...s }, r, {
|
|
68
|
+
tsConfig: i
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const a = j();
|
|
72
|
+
async function E(e) {
|
|
73
|
+
const t = await x(e), { messages: o, totalCruised: n } = F(t);
|
|
74
|
+
if (o.length === 0 && a.success("✅ No issues found!"), o.length > 0) {
|
|
75
|
+
const s = h(o);
|
|
76
|
+
a.error(s.join(`
|
|
77
|
+
`)), a.error(`Found ${o.length} violation(s). ${n} modules cruised.`);
|
|
78
|
+
}
|
|
79
|
+
return o.length;
|
|
80
|
+
}
|
|
81
|
+
function R(e, t, o) {
|
|
82
|
+
const n = e.at(-1) ?? "";
|
|
83
|
+
if (!/index\.(tsx?|jsx?|ts|js)$/.test(n))
|
|
84
|
+
return !1;
|
|
85
|
+
const s = e.at(-3) ?? "", r = t.at(-1);
|
|
86
|
+
return s === r && o === e.length - 2;
|
|
87
|
+
}
|
|
88
|
+
function b(e) {
|
|
89
|
+
const t = typeof e.output == "object" ? e.output : void 0, o = t?.modules ?? [], n = [];
|
|
90
|
+
for (const s of o) {
|
|
91
|
+
if (s.coreModule)
|
|
92
|
+
continue;
|
|
93
|
+
const r = s.dependents || [];
|
|
94
|
+
if (r.length <= 1)
|
|
95
|
+
continue;
|
|
96
|
+
const i = s.source.split("/"), u = i.length;
|
|
97
|
+
if (u <= 2)
|
|
98
|
+
continue;
|
|
99
|
+
const l = r.map((v) => v.split("/")), c = p(l), f = g([i, c]);
|
|
100
|
+
R(i, c, f) || f < u - 1 && n.push({
|
|
101
|
+
source: s.source,
|
|
102
|
+
target: c.join("/"),
|
|
103
|
+
rule: "not-shared-level",
|
|
104
|
+
severity: "info"
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
messages: n,
|
|
109
|
+
totalCruised: t?.summary.totalCruised ?? 0
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async function T(e) {
|
|
113
|
+
const t = await x(e), { messages: o, totalCruised: n } = b(t);
|
|
114
|
+
if (o.length === 0 && a.success("✅ No issues found!"), o.length > 0) {
|
|
115
|
+
const s = h(o);
|
|
116
|
+
a.info(s.join(`
|
|
117
|
+
`)), a.info(`Found ${o.length} violation(s). ${n} modules cruised.`);
|
|
118
|
+
}
|
|
119
|
+
return o.length;
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
T as a,
|
|
123
|
+
a as l,
|
|
124
|
+
E as v
|
|
125
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leancodepl/folder-structure-cruiser",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.4.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"folder-structure-cruiser": "./dist/bin.js"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@leancodepl/logger": "10.
|
|
22
|
+
"@leancodepl/logger": "10.4.0",
|
|
23
23
|
"chalk": ">=5.0.0",
|
|
24
24
|
"commander": "^14.0.0"
|
|
25
25
|
},
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import C from "chalk";
|
|
2
|
-
import { cruise as x } from "dependency-cruiser";
|
|
3
|
-
import P from "dependency-cruiser/config-utl/extract-depcruise-options";
|
|
4
|
-
import y from "dependency-cruiser/config-utl/extract-ts-config";
|
|
5
|
-
import M from "dependency-cruiser/config-utl/extract-webpack-resolve-config";
|
|
6
|
-
import { createCliLogger as $ } from "@leancodepl/logger/cli";
|
|
7
|
-
function m(e) {
|
|
8
|
-
if (e.length === 0) return [];
|
|
9
|
-
if (e.length === 1) return e[0];
|
|
10
|
-
const s = [], t = Math.min(...e.map((o) => o.length));
|
|
11
|
-
for (let o = 0; o < t; o++) {
|
|
12
|
-
const n = e[0][o];
|
|
13
|
-
if (e.every((r) => r[o] === n))
|
|
14
|
-
s.push(n);
|
|
15
|
-
else
|
|
16
|
-
break;
|
|
17
|
-
}
|
|
18
|
-
return s;
|
|
19
|
-
}
|
|
20
|
-
function g(e) {
|
|
21
|
-
return m(e).length;
|
|
22
|
-
}
|
|
23
|
-
function j(e) {
|
|
24
|
-
const s = typeof e.output == "object" ? e.output : void 0, t = s?.modules ?? [], o = [];
|
|
25
|
-
for (const n of t) {
|
|
26
|
-
if (n.coreModule)
|
|
27
|
-
continue;
|
|
28
|
-
const r = n.dependencies || [], i = n.source.split("/");
|
|
29
|
-
r.forEach((u) => {
|
|
30
|
-
const l = u.resolved.split("/"), c = g([i, l]);
|
|
31
|
-
(!c || c < i.length && l.length > c + 2) && o.push({
|
|
32
|
-
source: n.source,
|
|
33
|
-
target: u.resolved,
|
|
34
|
-
rule: "cross-feature-nested-imports",
|
|
35
|
-
severity: "error"
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
messages: o,
|
|
41
|
-
totalCruised: s?.summary.totalCruised ?? 0
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
const { bold: d } = C;
|
|
45
|
-
function p(e) {
|
|
46
|
-
return e.map((s) => `${s.rule}: ${d(s.source)} → ${d(s.target)}`);
|
|
47
|
-
}
|
|
48
|
-
async function h({
|
|
49
|
-
directories: e = [".*"],
|
|
50
|
-
configPath: s,
|
|
51
|
-
tsConfigPath: t,
|
|
52
|
-
webpackConfigPath: o
|
|
53
|
-
}) {
|
|
54
|
-
const n = await P(s), r = o ? await M(o) : void 0, i = t ? y(t) : void 0;
|
|
55
|
-
return await x(e, { ...n }, r, {
|
|
56
|
-
tsConfig: i
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
const a = $();
|
|
60
|
-
async function S(e) {
|
|
61
|
-
const s = await h(e), { messages: t, totalCruised: o } = j(s);
|
|
62
|
-
if (t.length === 0 && a.success("✅ No issues found!"), t.length > 0) {
|
|
63
|
-
const n = p(t);
|
|
64
|
-
a.error(n.join(`
|
|
65
|
-
`)), a.error(`Found ${t.length} violation(s). ${o} modules cruised.`);
|
|
66
|
-
}
|
|
67
|
-
return t.length;
|
|
68
|
-
}
|
|
69
|
-
function F(e, s, t) {
|
|
70
|
-
const o = e.at(-1) ?? "";
|
|
71
|
-
if (!/index\.(tsx?|jsx?|ts|js)$/.test(o))
|
|
72
|
-
return !1;
|
|
73
|
-
const n = e.at(-3) ?? "", r = s.at(-1);
|
|
74
|
-
return n === r && t === e.length - 2;
|
|
75
|
-
}
|
|
76
|
-
function L(e) {
|
|
77
|
-
const s = typeof e.output == "object" ? e.output : void 0, t = s?.modules ?? [], o = [];
|
|
78
|
-
for (const n of t) {
|
|
79
|
-
if (n.coreModule)
|
|
80
|
-
continue;
|
|
81
|
-
const r = n.dependents || [];
|
|
82
|
-
if (r.length <= 1)
|
|
83
|
-
continue;
|
|
84
|
-
const i = n.source.split("/"), u = i.length;
|
|
85
|
-
if (u <= 2)
|
|
86
|
-
continue;
|
|
87
|
-
const l = r.map((v) => v.split("/")), c = m(l), f = g([i, c]);
|
|
88
|
-
F(i, c, f) || f < u - 1 && o.push({
|
|
89
|
-
source: n.source,
|
|
90
|
-
target: c.join("/"),
|
|
91
|
-
rule: "not-shared-level",
|
|
92
|
-
severity: "info"
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
messages: o,
|
|
97
|
-
totalCruised: s?.summary.totalCruised ?? 0
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
async function D(e) {
|
|
101
|
-
const s = await h(e), { messages: t, totalCruised: o } = L(s);
|
|
102
|
-
if (t.length === 0 && a.success("✅ No issues found!"), t.length > 0) {
|
|
103
|
-
const n = p(t);
|
|
104
|
-
a.info(n.join(`
|
|
105
|
-
`)), a.info(`Found ${t.length} violation(s). ${o} modules cruised.`);
|
|
106
|
-
}
|
|
107
|
-
return t.length;
|
|
108
|
-
}
|
|
109
|
-
export {
|
|
110
|
-
D as a,
|
|
111
|
-
a as l,
|
|
112
|
-
S as v
|
|
113
|
-
};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
"use strict";const v=require("chalk"),x=require("dependency-cruiser"),P=require("dependency-cruiser/config-utl/extract-depcruise-options"),y=require("dependency-cruiser/config-utl/extract-ts-config"),F=require("dependency-cruiser/config-utl/extract-webpack-resolve-config"),M=require("@leancodepl/logger/cli");function g(e){if(e.length===0)return[];if(e.length===1)return e[0];const s=[],t=Math.min(...e.map(o=>o.length));for(let o=0;o<t;o++){const n=e[0][o];if(e.every(r=>r[o]===n))s.push(n);else break}return s}function m(e){return g(e).length}function $(e){const s=typeof e.output=="object"?e.output:void 0,t=s?.modules??[],o=[];for(const n of t){if(n.coreModule)continue;const r=n.dependencies||[],i=n.source.split("/");r.forEach(a=>{const l=a.resolved.split("/"),c=m([i,l]);(!c||c<i.length&&l.length>c+2)&&o.push({source:n.source,target:a.resolved,rule:"cross-feature-nested-imports",severity:"error"})})}return{messages:o,totalCruised:s?.summary.totalCruised??0}}const{bold:f}=v;function p(e){return e.map(s=>`${s.rule}: ${f(s.source)} → ${f(s.target)}`)}async function h({directories:e=[".*"],configPath:s,tsConfigPath:t,webpackConfigPath:o}){const n=await P(s),r=o?await F(o):void 0,i=t?y(t):void 0;return await x.cruise(e,{...n},r,{tsConfig:i})}const u=M.createCliLogger();async function j(e){const s=await h(e),{messages:t,totalCruised:o}=$(s);if(t.length===0&&u.success("✅ No issues found!"),t.length>0){const n=p(t);u.error(n.join(`
|
|
2
|
-
`)),u.error(`Found ${t.length} violation(s). ${o} modules cruised.`)}return t.length}function q(e,s,t){const o=e.at(-1)??"";if(!/index\.(tsx?|jsx?|ts|js)$/.test(o))return!1;const n=e.at(-3)??"",r=s.at(-1);return n===r&&t===e.length-2}function L(e){const s=typeof e.output=="object"?e.output:void 0,t=s?.modules??[],o=[];for(const n of t){if(n.coreModule)continue;const r=n.dependents||[];if(r.length<=1)continue;const i=n.source.split("/"),a=i.length;if(a<=2)continue;const l=r.map(C=>C.split("/")),c=g(l),d=m([i,c]);q(i,c,d)||d<a-1&&o.push({source:n.source,target:c.join("/"),rule:"not-shared-level",severity:"info"})}return{messages:o,totalCruised:s?.summary.totalCruised??0}}async function R(e){const s=await h(e),{messages:t,totalCruised:o}=L(s);if(t.length===0&&u.success("✅ No issues found!"),t.length>0){const n=p(t);u.info(n.join(`
|
|
3
|
-
`)),u.info(`Found ${t.length} violation(s). ${o} modules cruised.`)}return t.length}exports.logger=u;exports.validateCrossFeatureImports=j;exports.validateSharedComponent=R;
|