1+ const fs = require ( 'fs' ) ;
2+ const path = require ( 'path' ) ;
3+ const { execSync } = require ( 'child_process' ) ;
4+
5+ function validatePackages ( ) {
6+ const componentsDir = 'components' ;
7+ const apps = fs . readdirSync ( componentsDir ) . filter ( dir => {
8+ const packagePath = path . join ( componentsDir , dir , 'package.json' ) ;
9+ return fs . existsSync ( packagePath ) ;
10+ } ) ;
11+
12+ const results = {
13+ validated : [ ] ,
14+ failed : [ ] ,
15+ skipped : [ ]
16+ } ;
17+
18+ console . log ( `🔍 Validating ${ apps . length } component packages...\n` ) ;
19+
20+ for ( const app of apps ) {
21+ const packagePath = path . join ( componentsDir , app , 'package.json' ) ;
22+ let packageJson = null ;
23+ let packageName = app ; // Use app name as fallback
24+
25+ try {
26+ // Parse package.json
27+ const packageJsonContent = fs . readFileSync ( packagePath , 'utf8' ) ;
28+ packageJson = JSON . parse ( packageJsonContent ) ;
29+ packageName = packageJson . name || `${ app } (no name)` ;
30+
31+ // Only validate @pipedream /* packages with publishConfig
32+ if ( ! packageName || ! packageName . startsWith ( '@pipedream/' ) ) {
33+ results . skipped . push ( { app, packageName, reason : 'Not a @pipedream package' } ) ;
34+ continue ;
35+ }
36+
37+ if ( ! packageJson . publishConfig ?. access ) {
38+ results . skipped . push ( { app, packageName, reason : 'No publishConfig.access' } ) ;
39+ continue ;
40+ }
41+
42+ console . log ( `📦 Validating ${ packageName } ...` ) ;
43+
44+ // Run all validations
45+ validatePackageJson ( packageJson , app ) ;
46+ validateMainFile ( packageJson , app ) ;
47+ validateDependencies ( packageJson , app ) ;
48+ validateImport ( packageName , app , packageJson ) ;
49+
50+ results . validated . push ( { app, packageName } ) ;
51+ console . log ( `✅ ${ packageName } - VALID\n` ) ;
52+
53+ } catch ( error ) {
54+ results . failed . push ( {
55+ app,
56+ packageName,
57+ error : error . message
58+ } ) ;
59+ console . error ( `❌ ${ app } (${ packageName } ) - FAILED: ${ error . message } \n` ) ;
60+ }
61+ }
62+
63+ // Print summary
64+ printSummary ( results ) ;
65+
66+ // Exit with error if any validations failed
67+ if ( results . failed . length > 0 ) {
68+ console . error ( `\n💥 Validation failed for ${ results . failed . length } packages. See errors above.` ) ;
69+ process . exit ( 1 ) ;
70+ }
71+
72+ console . log ( `\n🎉 All ${ results . validated . length } packages validated successfully!` ) ;
73+ }
74+
75+ function validatePackageJson ( packageJson , app ) {
76+ const required = [ 'name' , 'version' , 'main' ] ;
77+
78+ for ( const field of required ) {
79+ if ( ! packageJson [ field ] ) {
80+ throw new Error ( `Missing required field: ${ field } ` ) ;
81+ }
82+ }
83+
84+ // Validate version format
85+ if ( ! / ^ \d + \. \d + \. \d + / . test ( packageJson . version ) ) {
86+ throw new Error ( `Invalid version format: ${ packageJson . version } ` ) ;
87+ }
88+
89+ // Validate publishConfig
90+ if ( ! packageJson . publishConfig ?. access ) {
91+ throw new Error ( 'Missing publishConfig.access for public package' ) ;
92+ }
93+ }
94+
95+ function validateMainFile ( packageJson , app ) {
96+ const mainFile = path . join ( 'components' , app , packageJson . main ) ;
97+
98+ if ( ! fs . existsSync ( mainFile ) ) {
99+ throw new Error ( `Main file not found: ${ packageJson . main } ` ) ;
100+ }
101+
102+ // Check if main file has basic export structure
103+ const content = fs . readFileSync ( mainFile , 'utf8' ) ;
104+ if ( ! content . includes ( 'export' ) && ! content . includes ( 'module.exports' ) ) {
105+ throw new Error ( `Main file ${ packageJson . main } has no exports` ) ;
106+ }
107+ }
108+
109+ function validateDependencies ( packageJson , app ) {
110+ if ( ! packageJson . dependencies ) return ;
111+
112+ // Check for common problematic dependencies
113+ const problematic = Object . keys ( packageJson . dependencies ) . filter ( dep => {
114+ // Add checks for dependencies that commonly cause issues
115+ return dep . includes ( 'node-gyp' ) || dep . includes ( 'native' ) ;
116+ } ) ;
117+
118+ if ( problematic . length > 0 ) {
119+ console . warn ( `⚠️ Potentially problematic dependencies: ${ problematic . join ( ', ' ) } ` ) ;
120+ }
121+
122+ // Validate @pipedream /platform version if present
123+ const platformDep = packageJson . dependencies [ '@pipedream/platform' ] ;
124+ if ( platformDep && ! platformDep . match ( / ^ [ \^ ~ ] ? \d + \. \d + \. \d + / ) ) {
125+ throw new Error ( `Invalid @pipedream/platform version: ${ platformDep } ` ) ;
126+ }
127+ }
128+
129+ function validateImport ( packageName , app ) {
130+ // Create a temporary test file to validate import
131+ const testFile = path . join ( 'components' , app , '__import_test__.mjs' ) ;
132+ const testContent = `
133+ try {
134+ const pkg = await import("${ packageName } ");
135+ console.log("Import successful for ${ packageName } ");
136+ process.exit(0);
137+ } catch (error) {
138+ console.error("Import failed for ${ packageName } :", error.message);
139+ process.exit(1);
140+ }` ;
141+
142+ try {
143+ fs . writeFileSync ( testFile , testContent ) ;
144+
145+ // Run the import test
146+ execSync ( `node ${ testFile } ` , {
147+ stdio : 'pipe' ,
148+ cwd : process . cwd ( ) ,
149+ timeout : 10000 // 10 second timeout
150+ } ) ;
151+
152+ } catch ( error ) {
153+ throw new Error ( `Import test failed: ${ error . message } ` ) ;
154+ } finally {
155+ // Clean up test file
156+ if ( fs . existsSync ( testFile ) ) {
157+ fs . unlinkSync ( testFile ) ;
158+ }
159+ }
160+ }
161+
162+ function printSummary ( results ) {
163+ console . log ( '\n📊 VALIDATION SUMMARY' ) ;
164+ console . log ( '=' . repeat ( 50 ) ) ;
165+ console . log ( `✅ Validated: ${ results . validated . length } ` ) ;
166+ console . log ( `❌ Failed: ${ results . failed . length } ` ) ;
167+ console . log ( `⏭️ Skipped: ${ results . skipped . length } ` ) ;
168+
169+ if ( results . failed . length > 0 ) {
170+ console . log ( '\n❌ FAILED PACKAGES:' ) ;
171+ results . failed . forEach ( ( { app, packageName, error } ) => {
172+ console . log ( ` • ${ packageName } (${ app } ): ${ error } ` ) ;
173+ } ) ;
174+ }
175+
176+ if ( results . validated . length > 0 ) {
177+ console . log ( '\n✅ VALIDATED PACKAGES:' ) ;
178+ results . validated . forEach ( ( { packageName } ) => {
179+ console . log ( ` • ${ packageName } ` ) ;
180+ } ) ;
181+ }
182+ }
183+
184+ // Run validation
185+ validatePackages ( ) ;
0 commit comments