1+ const fs = require ( 'fs' ) ;
2+
3+ class PackageJsonDependencyComparator {
4+ /**
5+ * Compares dependencies between two package.json files
6+ * @param {Object } packageJson1 - First package.json object
7+ * @param {Object } packageJson2 - Second package.json object
8+ * @returns {Object } Report containing mismatched dependencies
9+ */
10+ compareDependencies ( packageJson1 , packageJson2 ) {
11+ const report = {
12+ mismatches : [ ] ,
13+ missingInFirst : [ ] ,
14+ missingInSecond : [ ]
15+ } ;
16+ console . log ( 'Comparing dependencies:' , packageJson1 , packageJson2 ) ;
17+
18+ // Get all dependencies from both files
19+ const deps1 = this . _getAllDependencies ( packageJson1 ) ;
20+ const deps2 = this . _getAllDependencies ( packageJson2 ) ;
21+ console . log ( 'Dependencies:' , deps1 , deps2 ) ;
22+
23+ // Compare dependencies
24+ for ( const [ dep , version1 ] of Object . entries ( deps1 ) ) {
25+ const version2 = deps2 [ dep ] ;
26+ console . log ( 'Comparing dependency:' , dep , version1 , version2 ) ;
27+
28+ if ( version2 === undefined ) {
29+ continue ; // we don't care about missing dependencies
30+ } else if ( version1 !== version2 ) {
31+ report . mismatches . push ( {
32+ dependency : dep ,
33+ version1,
34+ version2
35+ } ) ;
36+ }
37+ }
38+
39+ // Find dependencies that exist only in second file
40+ for ( const [ dep ] of Object . entries ( deps2 ) ) {
41+ if ( deps1 [ dep ] === undefined ) {
42+ continue ; // we don't care about missing dependencies
43+ }
44+ }
45+
46+ return report ;
47+ }
48+
49+ /**
50+ * Extracts all dependencies from a package.json object
51+ * @param {Object } packageJson - package.json object
52+ * @returns {Object } Combined dependencies object
53+ */
54+ _getAllDependencies ( packageJson ) {
55+ return {
56+ ...packageJson . dependencies || { } ,
57+ ...packageJson . devDependencies || { } ,
58+ ...packageJson . peerDependencies || { } ,
59+ ...packageJson . optionalDependencies || { }
60+ } ;
61+ }
62+
63+ /**
64+ * Formats the comparison report into a readable string
65+ * @param {Object } report - Comparison report
66+ * @returns {string } Formatted report
67+ */
68+ formatReport ( report ) {
69+ let parts = [ ] ;
70+
71+ if ( report . mismatches . length > 0 ) {
72+ const mismatchItems = report . mismatches . map ( ( { dependency, version1, version2 } ) =>
73+ `${ dependency } :${ version1 } vs${ version2 } `
74+ ) . join ( ', ' ) ;
75+ parts . push ( `Mismatched against layer: [${ mismatchItems } ]` ) ;
76+ }
77+
78+ if ( report . missingInFirst . length > 0 ) {
79+ const missingFirstItems = report . missingInFirst . map ( ( { dependency, version } ) =>
80+ `${ dependency } :${ version } `
81+ ) . join ( ', ' ) ;
82+ parts . push ( `Missing in First: [${ missingFirstItems } ]` ) ;
83+ }
84+
85+ if ( report . missingInSecond . length > 0 ) {
86+ const missingSecondItems = report . missingInSecond . map ( ( { dependency, version } ) =>
87+ `${ dependency } :${ version } `
88+ ) . join ( ', ' ) ;
89+ parts . push ( `Missing in Second: [${ missingSecondItems } ]` ) ;
90+ }
91+
92+ return parts . length > 0 ? parts . join ( ' | ' ) : 'No differences found between package.json files.' ;
93+ }
94+ }
95+
96+ class LayerDependencyAnalysis {
97+ constructor ( comparator ) {
98+ this . comparator = comparator ;
99+ }
100+
101+ run ( layerPackageJson , domainPackageJsons ) {
102+ console . log ( 'Running layer dependency analysis' ) ;
103+
104+ const reports = domainPackageJsons . map ( domainPackageJson => {
105+ return {
106+ project : domainPackageJson . project ,
107+ report : this . comparator . compareDependencies ( layerPackageJson , domainPackageJson . packageJson )
108+ } ;
109+ } ) ;
110+
111+ const reportsWithWarnings = reports . filter ( report => report . report . mismatches . length > 0 ) ;
112+
113+ if ( reportsWithWarnings . length > 0 ) {
114+ console . log ( 'Reports with mismatched dependencies:' ) ;
115+ reportsWithWarnings . forEach ( report => {
116+ // output warning to github actions
117+ console . log ( `::warning file=${ report . project } /package.json::${ this . comparator . formatReport ( report . report ) } ` ) ;
118+ } ) ;
119+ } else {
120+ console . log ( 'No mismatched dependencies found' ) ;
121+ }
122+ }
123+ }
124+
125+ function main ( ) {
126+ if ( process . argv . length < 4 ) {
127+ console . error ( 'Usage: node layer_dependency_analysis.js <layer-package-json> <domains>' ) ;
128+ process . exit ( 1 ) ;
129+ }
130+
131+ console . log ( 'Layer package.json:' , process . argv [ 2 ] ) ;
132+ console . log ( 'Domains:' , process . argv [ 3 ] ) ;
133+ console . log ( 'Current working directory:' , process . cwd ( ) ) ;
134+
135+ // Example usage:
136+ const comparator = new PackageJsonDependencyComparator ( ) ;
137+
138+ const layerDependencyAnalysis = new LayerDependencyAnalysis ( comparator ) ;
139+
140+ console . log ( 'Reading layer package.json:' , process . cwd ( ) + '/' + process . argv [ 2 ] ) ;
141+ const layerPackageJson = JSON . parse ( fs . readFileSync ( process . cwd ( ) + '/' + process . argv [ 2 ] , 'utf8' ) ) ;
142+ const domains = JSON . parse ( process . argv [ 3 ] ) ; // {"include": [{"project": "domain1"}, {"project": "domain2"}] }
143+
144+ const domainPackageJsons = domains . include . filter ( domain => domain . project != '.' ) . map ( domain => {
145+ return {
146+ project : domain . project ,
147+ packageJson : JSON . parse ( fs . readFileSync ( process . cwd ( ) + '/domains/' + domain . project + '/package.json' , 'utf8' ) )
148+ } ;
149+ } ) ;
150+
151+ // print the test plan
152+ console . log ( 'Test plan:' ) ;
153+ console . log ( 'Layer package.json:' , layerPackageJson ) ;
154+ console . log ( 'Domains:' , domains ) ;
155+ console . log ( 'Domain package.json:' , JSON . stringify ( domainPackageJsons , null , 2 ) ) ;
156+
157+ layerDependencyAnalysis . run ( layerPackageJson , domainPackageJsons ) ;
158+ }
159+
160+ main ( ) ;
0 commit comments