@friggframework/devtools 2.0.0--canary.474.edb48ba.0 → 2.0.0--canary.482.c063a2a.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/infrastructure/docs/PRE-DEPLOYMENT-HEALTH-CHECK-SPEC.md +1317 -0
- package/infrastructure/domains/database/migration-builder.js +64 -2
- package/infrastructure/domains/shared/resource-discovery.enhanced.test.js +306 -0
- package/infrastructure/domains/shared/resource-discovery.js +31 -2
- package/infrastructure/domains/shared/validation/plugin-validator.js +187 -0
- package/infrastructure/domains/shared/validation/plugin-validator.test.js +323 -0
- package/infrastructure/infrastructure-composer.js +22 -0
- package/package.json +6 -6
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Validator
|
|
3
|
+
*
|
|
4
|
+
* Validation Layer - Hexagonal Architecture
|
|
5
|
+
*
|
|
6
|
+
* Validates serverless plugin configuration to detect conflicts and provide
|
|
7
|
+
* migration guidance for packaging plugins (serverless-esbuild vs serverless-jetpack).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Detect conflicting packaging plugins in serverless configuration
|
|
12
|
+
*
|
|
13
|
+
* @param {Array<string>} plugins - List of serverless plugins
|
|
14
|
+
* @returns {Object} Validation result with conflict detection
|
|
15
|
+
*/
|
|
16
|
+
function detectConflictingPlugins(plugins = []) {
|
|
17
|
+
const hasEsbuild = plugins.includes('serverless-esbuild');
|
|
18
|
+
const hasJetpack = plugins.includes('serverless-jetpack');
|
|
19
|
+
|
|
20
|
+
const result = {
|
|
21
|
+
hasConflict: false,
|
|
22
|
+
hasEsbuild,
|
|
23
|
+
hasJetpack,
|
|
24
|
+
warnings: [],
|
|
25
|
+
recommendations: [],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Check for explicit conflict - both plugins present
|
|
29
|
+
if (hasEsbuild && hasJetpack) {
|
|
30
|
+
result.hasConflict = true;
|
|
31
|
+
result.warnings.push(
|
|
32
|
+
'Both serverless-esbuild and serverless-jetpack are configured. ' +
|
|
33
|
+
'These plugins have overlapping functionality for Lambda packaging.'
|
|
34
|
+
);
|
|
35
|
+
result.recommendations.push(
|
|
36
|
+
'Remove serverless-jetpack from your serverless.yml plugins array.',
|
|
37
|
+
'The Frigg framework now uses serverless-esbuild as the standard bundling solution.',
|
|
38
|
+
'See docs/reference/aws-sdk-v3-osls-migration.md for migration guidance.'
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check for legacy jetpack usage (jetpack without esbuild)
|
|
43
|
+
if (hasJetpack && !hasEsbuild) {
|
|
44
|
+
result.warnings.push(
|
|
45
|
+
'serverless-jetpack is a legacy packaging plugin. ' +
|
|
46
|
+
'Frigg framework now recommends serverless-esbuild for better performance and compatibility.'
|
|
47
|
+
);
|
|
48
|
+
result.recommendations.push(
|
|
49
|
+
'Consider migrating to serverless-esbuild for improved build times and tree-shaking.',
|
|
50
|
+
'Update your serverless.yml to use serverless-esbuild instead of serverless-jetpack.',
|
|
51
|
+
'See docs/reference/aws-sdk-v3-osls-migration.md for migration guidance.'
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Validate esbuild is present (standard for Frigg)
|
|
56
|
+
if (!hasEsbuild && !hasJetpack) {
|
|
57
|
+
result.warnings.push(
|
|
58
|
+
'No packaging plugin detected. Serverless will use default packaging which may be slow.'
|
|
59
|
+
);
|
|
60
|
+
result.recommendations.push(
|
|
61
|
+
'Add serverless-esbuild to your serverless.yml plugins array for optimized Lambda bundling.'
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Validate and clean plugin list by removing conflicts
|
|
70
|
+
*
|
|
71
|
+
* Automatically removes serverless-jetpack if serverless-esbuild is present.
|
|
72
|
+
* This provides automatic migration for users with legacy configurations.
|
|
73
|
+
*
|
|
74
|
+
* @param {Array<string>} plugins - List of serverless plugins
|
|
75
|
+
* @param {Object} options - Validation options
|
|
76
|
+
* @param {boolean} options.autoFix - Automatically fix conflicts by removing jetpack (default: true)
|
|
77
|
+
* @param {boolean} options.silent - Suppress console warnings (default: false)
|
|
78
|
+
* @returns {Object} Result with cleaned plugins and validation info
|
|
79
|
+
*/
|
|
80
|
+
function validateAndCleanPlugins(plugins = [], options = {}) {
|
|
81
|
+
const { autoFix = true, silent = false } = options;
|
|
82
|
+
const validation = detectConflictingPlugins(plugins);
|
|
83
|
+
|
|
84
|
+
let cleanedPlugins = [...plugins];
|
|
85
|
+
let modified = false;
|
|
86
|
+
|
|
87
|
+
// Auto-fix: Remove jetpack if esbuild is present
|
|
88
|
+
if (validation.hasConflict && autoFix) {
|
|
89
|
+
cleanedPlugins = plugins.filter(p => p !== 'serverless-jetpack');
|
|
90
|
+
modified = true;
|
|
91
|
+
|
|
92
|
+
if (!silent) {
|
|
93
|
+
console.warn('\n⚠️ Plugin Conflict Detected and Auto-Fixed:');
|
|
94
|
+
console.warn(' Removed serverless-jetpack (using serverless-esbuild instead)');
|
|
95
|
+
console.warn(' The Frigg framework uses serverless-esbuild as the standard bundling solution.\n');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Show warnings for other cases
|
|
100
|
+
if (!silent && validation.warnings.length > 0 && !modified) {
|
|
101
|
+
console.warn('\n⚠️ Plugin Configuration Warning:');
|
|
102
|
+
validation.warnings.forEach(warning => {
|
|
103
|
+
console.warn(` ${warning}`);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (validation.recommendations.length > 0) {
|
|
107
|
+
console.warn('\n💡 Recommendations:');
|
|
108
|
+
validation.recommendations.forEach(rec => {
|
|
109
|
+
console.warn(` • ${rec}`);
|
|
110
|
+
});
|
|
111
|
+
console.warn('');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
plugins: cleanedPlugins,
|
|
117
|
+
modified,
|
|
118
|
+
validation,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if a serverless definition has proper packaging configuration
|
|
124
|
+
*
|
|
125
|
+
* @param {Object} serverlessDefinition - Serverless framework definition
|
|
126
|
+
* @returns {Object} Validation result
|
|
127
|
+
*/
|
|
128
|
+
function validatePackagingConfiguration(serverlessDefinition) {
|
|
129
|
+
const plugins = serverlessDefinition?.plugins || [];
|
|
130
|
+
const custom = serverlessDefinition?.custom || {};
|
|
131
|
+
|
|
132
|
+
const result = {
|
|
133
|
+
valid: true,
|
|
134
|
+
errors: [],
|
|
135
|
+
warnings: [],
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const hasEsbuild = plugins.includes('serverless-esbuild');
|
|
139
|
+
const hasEsbuildConfig = custom.esbuild !== undefined;
|
|
140
|
+
|
|
141
|
+
// If esbuild plugin is present, ensure it's configured
|
|
142
|
+
if (hasEsbuild && !hasEsbuildConfig) {
|
|
143
|
+
result.warnings.push(
|
|
144
|
+
'serverless-esbuild plugin is present but custom.esbuild configuration is missing. ' +
|
|
145
|
+
'Using default esbuild settings.'
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check for external dependencies in esbuild config
|
|
150
|
+
if (hasEsbuildConfig && hasEsbuild) {
|
|
151
|
+
const external = custom.esbuild.external || [];
|
|
152
|
+
|
|
153
|
+
// Validate that AWS SDK and Prisma are externalized
|
|
154
|
+
const hasAwsSdkExternal = external.some(e =>
|
|
155
|
+
e === '@aws-sdk/*' || e === 'aws-sdk' || e.startsWith('@aws-sdk/')
|
|
156
|
+
);
|
|
157
|
+
const hasPrismaExternal = external.some(e =>
|
|
158
|
+
e === '@prisma/client' || e === 'prisma' || e.startsWith('.prisma')
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (!hasAwsSdkExternal) {
|
|
162
|
+
result.warnings.push(
|
|
163
|
+
'AWS SDK is not externalized in esbuild config. ' +
|
|
164
|
+
'Consider adding "@aws-sdk/*" to external array to reduce bundle size.'
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!hasPrismaExternal) {
|
|
169
|
+
result.warnings.push(
|
|
170
|
+
'Prisma is not externalized in esbuild config. ' +
|
|
171
|
+
'Consider adding "@prisma/client" to external array since it\'s provided via Lambda Layer.'
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (result.errors.length > 0) {
|
|
177
|
+
result.valid = false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = {
|
|
184
|
+
detectConflictingPlugins,
|
|
185
|
+
validateAndCleanPlugins,
|
|
186
|
+
validatePackagingConfiguration,
|
|
187
|
+
};
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Validator Tests
|
|
3
|
+
*
|
|
4
|
+
* Test suite following TDD best practices for plugin conflict detection
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
detectConflictingPlugins,
|
|
9
|
+
validateAndCleanPlugins,
|
|
10
|
+
validatePackagingConfiguration,
|
|
11
|
+
} = require('./plugin-validator');
|
|
12
|
+
|
|
13
|
+
describe('Plugin Validator', () => {
|
|
14
|
+
describe('detectConflictingPlugins', () => {
|
|
15
|
+
it('should detect conflict when both esbuild and jetpack are present', () => {
|
|
16
|
+
const plugins = ['serverless-esbuild', 'serverless-jetpack'];
|
|
17
|
+
const result = detectConflictingPlugins(plugins);
|
|
18
|
+
|
|
19
|
+
expect(result.hasConflict).toBe(true);
|
|
20
|
+
expect(result.hasEsbuild).toBe(true);
|
|
21
|
+
expect(result.hasJetpack).toBe(true);
|
|
22
|
+
expect(result.warnings).toHaveLength(1);
|
|
23
|
+
expect(result.warnings[0]).toContain('overlapping functionality');
|
|
24
|
+
expect(result.recommendations).toHaveLength(3);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should not detect conflict when only esbuild is present', () => {
|
|
28
|
+
const plugins = ['serverless-esbuild'];
|
|
29
|
+
const result = detectConflictingPlugins(plugins);
|
|
30
|
+
|
|
31
|
+
expect(result.hasConflict).toBe(false);
|
|
32
|
+
expect(result.hasEsbuild).toBe(true);
|
|
33
|
+
expect(result.hasJetpack).toBe(false);
|
|
34
|
+
expect(result.warnings).toHaveLength(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should warn about legacy jetpack usage', () => {
|
|
38
|
+
const plugins = ['serverless-jetpack'];
|
|
39
|
+
const result = detectConflictingPlugins(plugins);
|
|
40
|
+
|
|
41
|
+
expect(result.hasConflict).toBe(false);
|
|
42
|
+
expect(result.hasEsbuild).toBe(false);
|
|
43
|
+
expect(result.hasJetpack).toBe(true);
|
|
44
|
+
expect(result.warnings).toHaveLength(1);
|
|
45
|
+
expect(result.warnings[0]).toContain('legacy packaging plugin');
|
|
46
|
+
expect(result.recommendations.length).toBeGreaterThan(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should warn when no packaging plugin is present', () => {
|
|
50
|
+
const plugins = ['serverless-offline'];
|
|
51
|
+
const result = detectConflictingPlugins(plugins);
|
|
52
|
+
|
|
53
|
+
expect(result.hasConflict).toBe(false);
|
|
54
|
+
expect(result.hasEsbuild).toBe(false);
|
|
55
|
+
expect(result.hasJetpack).toBe(false);
|
|
56
|
+
expect(result.warnings).toHaveLength(1);
|
|
57
|
+
expect(result.warnings[0]).toContain('No packaging plugin detected');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should handle empty plugin array', () => {
|
|
61
|
+
const result = detectConflictingPlugins([]);
|
|
62
|
+
|
|
63
|
+
expect(result.hasConflict).toBe(false);
|
|
64
|
+
expect(result.hasEsbuild).toBe(false);
|
|
65
|
+
expect(result.hasJetpack).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should handle undefined plugin array', () => {
|
|
69
|
+
const result = detectConflictingPlugins();
|
|
70
|
+
|
|
71
|
+
expect(result.hasConflict).toBe(false);
|
|
72
|
+
expect(result.hasEsbuild).toBe(false);
|
|
73
|
+
expect(result.hasJetpack).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('validateAndCleanPlugins', () => {
|
|
78
|
+
it('should auto-fix conflict by removing jetpack when autoFix is true', () => {
|
|
79
|
+
const plugins = ['serverless-esbuild', 'serverless-jetpack', 'serverless-offline'];
|
|
80
|
+
const result = validateAndCleanPlugins(plugins, { autoFix: true, silent: true });
|
|
81
|
+
|
|
82
|
+
expect(result.modified).toBe(true);
|
|
83
|
+
expect(result.plugins).toEqual(['serverless-esbuild', 'serverless-offline']);
|
|
84
|
+
expect(result.plugins).not.toContain('serverless-jetpack');
|
|
85
|
+
expect(result.validation.hasConflict).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should not modify plugins when autoFix is false', () => {
|
|
89
|
+
const plugins = ['serverless-esbuild', 'serverless-jetpack'];
|
|
90
|
+
const result = validateAndCleanPlugins(plugins, { autoFix: false, silent: true });
|
|
91
|
+
|
|
92
|
+
expect(result.modified).toBe(false);
|
|
93
|
+
expect(result.plugins).toEqual(plugins);
|
|
94
|
+
expect(result.validation.hasConflict).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should not modify plugins when no conflict exists', () => {
|
|
98
|
+
const plugins = ['serverless-esbuild', 'serverless-offline'];
|
|
99
|
+
const result = validateAndCleanPlugins(plugins, { autoFix: true, silent: true });
|
|
100
|
+
|
|
101
|
+
expect(result.modified).toBe(false);
|
|
102
|
+
expect(result.plugins).toEqual(plugins);
|
|
103
|
+
expect(result.validation.hasConflict).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should preserve order of non-conflicting plugins', () => {
|
|
107
|
+
const plugins = [
|
|
108
|
+
'serverless-offline',
|
|
109
|
+
'serverless-esbuild',
|
|
110
|
+
'serverless-jetpack',
|
|
111
|
+
'@friggframework/serverless-plugin',
|
|
112
|
+
];
|
|
113
|
+
const result = validateAndCleanPlugins(plugins, { autoFix: true, silent: true });
|
|
114
|
+
|
|
115
|
+
expect(result.plugins).toEqual([
|
|
116
|
+
'serverless-offline',
|
|
117
|
+
'serverless-esbuild',
|
|
118
|
+
'@friggframework/serverless-plugin',
|
|
119
|
+
]);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should handle empty plugin array', () => {
|
|
123
|
+
const result = validateAndCleanPlugins([], { autoFix: true, silent: true });
|
|
124
|
+
|
|
125
|
+
expect(result.modified).toBe(false);
|
|
126
|
+
expect(result.plugins).toEqual([]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should use default options when none provided', () => {
|
|
130
|
+
const plugins = ['serverless-esbuild', 'serverless-jetpack'];
|
|
131
|
+
|
|
132
|
+
// Suppress console output for test
|
|
133
|
+
const originalWarn = console.warn;
|
|
134
|
+
console.warn = jest.fn();
|
|
135
|
+
|
|
136
|
+
const result = validateAndCleanPlugins(plugins);
|
|
137
|
+
|
|
138
|
+
expect(result.modified).toBe(true);
|
|
139
|
+
expect(result.plugins).not.toContain('serverless-jetpack');
|
|
140
|
+
expect(console.warn).toHaveBeenCalled();
|
|
141
|
+
|
|
142
|
+
console.warn = originalWarn;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should not log warnings when silent is true', () => {
|
|
146
|
+
const plugins = ['serverless-esbuild', 'serverless-jetpack'];
|
|
147
|
+
const originalWarn = console.warn;
|
|
148
|
+
console.warn = jest.fn();
|
|
149
|
+
|
|
150
|
+
validateAndCleanPlugins(plugins, { autoFix: true, silent: true });
|
|
151
|
+
|
|
152
|
+
expect(console.warn).not.toHaveBeenCalled();
|
|
153
|
+
|
|
154
|
+
console.warn = originalWarn;
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('validatePackagingConfiguration', () => {
|
|
159
|
+
it('should validate a properly configured esbuild setup', () => {
|
|
160
|
+
const serverlessDefinition = {
|
|
161
|
+
plugins: ['serverless-esbuild'],
|
|
162
|
+
custom: {
|
|
163
|
+
esbuild: {
|
|
164
|
+
bundle: true,
|
|
165
|
+
external: ['@aws-sdk/*', '@prisma/client'],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const result = validatePackagingConfiguration(serverlessDefinition);
|
|
171
|
+
|
|
172
|
+
expect(result.valid).toBe(true);
|
|
173
|
+
expect(result.errors).toHaveLength(0);
|
|
174
|
+
expect(result.warnings).toHaveLength(0);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should warn when esbuild plugin is present but config is missing', () => {
|
|
178
|
+
const serverlessDefinition = {
|
|
179
|
+
plugins: ['serverless-esbuild'],
|
|
180
|
+
custom: {},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const result = validatePackagingConfiguration(serverlessDefinition);
|
|
184
|
+
|
|
185
|
+
expect(result.valid).toBe(true);
|
|
186
|
+
expect(result.warnings).toHaveLength(1);
|
|
187
|
+
expect(result.warnings[0]).toContain('custom.esbuild configuration is missing');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should warn when AWS SDK is not externalized', () => {
|
|
191
|
+
const serverlessDefinition = {
|
|
192
|
+
plugins: ['serverless-esbuild'],
|
|
193
|
+
custom: {
|
|
194
|
+
esbuild: {
|
|
195
|
+
bundle: true,
|
|
196
|
+
external: ['@prisma/client'],
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const result = validatePackagingConfiguration(serverlessDefinition);
|
|
202
|
+
|
|
203
|
+
expect(result.valid).toBe(true);
|
|
204
|
+
expect(result.warnings.some(w => w.includes('AWS SDK'))).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('should warn when Prisma is not externalized', () => {
|
|
208
|
+
const serverlessDefinition = {
|
|
209
|
+
plugins: ['serverless-esbuild'],
|
|
210
|
+
custom: {
|
|
211
|
+
esbuild: {
|
|
212
|
+
bundle: true,
|
|
213
|
+
external: ['@aws-sdk/*'],
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const result = validatePackagingConfiguration(serverlessDefinition);
|
|
219
|
+
|
|
220
|
+
expect(result.valid).toBe(true);
|
|
221
|
+
expect(result.warnings.some(w => w.includes('Prisma'))).toBe(true);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should accept aws-sdk as valid AWS SDK externalization', () => {
|
|
225
|
+
const serverlessDefinition = {
|
|
226
|
+
plugins: ['serverless-esbuild'],
|
|
227
|
+
custom: {
|
|
228
|
+
esbuild: {
|
|
229
|
+
external: ['aws-sdk', '@prisma/client'],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const result = validatePackagingConfiguration(serverlessDefinition);
|
|
235
|
+
|
|
236
|
+
expect(result.warnings.some(w => w.includes('AWS SDK'))).toBe(false);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should accept .prisma/* as valid Prisma externalization', () => {
|
|
240
|
+
const serverlessDefinition = {
|
|
241
|
+
plugins: ['serverless-esbuild'],
|
|
242
|
+
custom: {
|
|
243
|
+
esbuild: {
|
|
244
|
+
external: ['@aws-sdk/*', '.prisma/*'],
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const result = validatePackagingConfiguration(serverlessDefinition);
|
|
250
|
+
|
|
251
|
+
expect(result.warnings.some(w => w.includes('Prisma'))).toBe(false);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('should handle serverless definition without plugins', () => {
|
|
255
|
+
const serverlessDefinition = {
|
|
256
|
+
custom: {},
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const result = validatePackagingConfiguration(serverlessDefinition);
|
|
260
|
+
|
|
261
|
+
expect(result.valid).toBe(true);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should handle empty serverless definition', () => {
|
|
265
|
+
const result = validatePackagingConfiguration({});
|
|
266
|
+
|
|
267
|
+
expect(result.valid).toBe(true);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe('Integration Tests', () => {
|
|
272
|
+
it('should handle complete migration scenario from jetpack to esbuild', () => {
|
|
273
|
+
const plugins = [
|
|
274
|
+
'serverless-offline',
|
|
275
|
+
'serverless-jetpack',
|
|
276
|
+
'serverless-esbuild',
|
|
277
|
+
'@friggframework/serverless-plugin',
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
const result = validateAndCleanPlugins(plugins, { autoFix: true, silent: true });
|
|
281
|
+
|
|
282
|
+
expect(result.modified).toBe(true);
|
|
283
|
+
expect(result.plugins).toEqual([
|
|
284
|
+
'serverless-offline',
|
|
285
|
+
'serverless-esbuild',
|
|
286
|
+
'@friggframework/serverless-plugin',
|
|
287
|
+
]);
|
|
288
|
+
expect(result.validation.hasConflict).toBe(true);
|
|
289
|
+
expect(result.validation.hasEsbuild).toBe(true);
|
|
290
|
+
expect(result.validation.hasJetpack).toBe(true);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should provide helpful recommendations for legacy configurations', () => {
|
|
294
|
+
const plugins = ['serverless-jetpack'];
|
|
295
|
+
const result = validateAndCleanPlugins(plugins, { autoFix: false, silent: true });
|
|
296
|
+
|
|
297
|
+
expect(result.validation.recommendations).toContain(
|
|
298
|
+
'Consider migrating to serverless-esbuild for improved build times and tree-shaking.'
|
|
299
|
+
);
|
|
300
|
+
expect(
|
|
301
|
+
result.validation.recommendations.some(r => r.includes('migration guidance'))
|
|
302
|
+
).toBe(true);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('should work with Frigg standard plugin configuration', () => {
|
|
306
|
+
const standardPlugins = [
|
|
307
|
+
'serverless-esbuild',
|
|
308
|
+
'serverless-offline-sqs',
|
|
309
|
+
'serverless-offline',
|
|
310
|
+
'@friggframework/serverless-plugin',
|
|
311
|
+
];
|
|
312
|
+
|
|
313
|
+
const result = validateAndCleanPlugins(standardPlugins, {
|
|
314
|
+
autoFix: true,
|
|
315
|
+
silent: true,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
expect(result.modified).toBe(false);
|
|
319
|
+
expect(result.plugins).toEqual(standardPlugins);
|
|
320
|
+
expect(result.validation.hasConflict).toBe(false);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
});
|
|
@@ -21,6 +21,7 @@ const { IntegrationBuilder } = require('./domains/integration/integration-builde
|
|
|
21
21
|
const { modifyHandlerPaths } = require('./domains/shared/utilities/handler-path-resolver');
|
|
22
22
|
const { createBaseDefinition } = require('./domains/shared/utilities/base-definition-factory');
|
|
23
23
|
const { ensurePrismaLayerExists } = require('./domains/shared/utilities/prisma-layer-manager');
|
|
24
|
+
const { validateAndCleanPlugins, validatePackagingConfiguration } = require('./domains/shared/validation/plugin-validator');
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Compose serverless definition using domain builders
|
|
@@ -76,6 +77,27 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
76
77
|
|
|
77
78
|
Object.assign(definition.custom, merged.custom);
|
|
78
79
|
|
|
80
|
+
// Validate and clean plugins (detect conflicts, auto-fix if needed)
|
|
81
|
+
const pluginValidation = validateAndCleanPlugins(definition.plugins, {
|
|
82
|
+
autoFix: true,
|
|
83
|
+
silent: false,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (pluginValidation.modified) {
|
|
87
|
+
definition.plugins = pluginValidation.plugins;
|
|
88
|
+
console.log(' ✓ Plugin configuration auto-fixed');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Validate packaging configuration
|
|
92
|
+
const packagingValidation = validatePackagingConfiguration(definition);
|
|
93
|
+
if (!packagingValidation.valid) {
|
|
94
|
+
console.warn('⚠️ Packaging configuration issues detected:');
|
|
95
|
+
packagingValidation.errors.forEach(err => console.warn(` ✗ ${err}`));
|
|
96
|
+
}
|
|
97
|
+
if (packagingValidation.warnings.length > 0) {
|
|
98
|
+
packagingValidation.warnings.forEach(warn => console.warn(` ℹ ${warn}`));
|
|
99
|
+
}
|
|
100
|
+
|
|
79
101
|
// Modify handler paths for offline mode
|
|
80
102
|
definition.functions = modifyHandlerPaths(definition.functions);
|
|
81
103
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/devtools",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.482.c063a2a.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-ec2": "^3.835.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.835.0",
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"@babel/eslint-parser": "^7.18.9",
|
|
13
13
|
"@babel/parser": "^7.25.3",
|
|
14
14
|
"@babel/traverse": "^7.25.3",
|
|
15
|
-
"@friggframework/schemas": "2.0.0--canary.
|
|
16
|
-
"@friggframework/test": "2.0.0--canary.
|
|
15
|
+
"@friggframework/schemas": "2.0.0--canary.482.c063a2a.0",
|
|
16
|
+
"@friggframework/test": "2.0.0--canary.482.c063a2a.0",
|
|
17
17
|
"@hapi/boom": "^10.0.1",
|
|
18
18
|
"@inquirer/prompts": "^5.3.8",
|
|
19
19
|
"axios": "^1.7.2",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"serverless-http": "^2.7.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
39
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
38
|
+
"@friggframework/eslint-config": "2.0.0--canary.482.c063a2a.0",
|
|
39
|
+
"@friggframework/prettier-config": "2.0.0--canary.482.c063a2a.0",
|
|
40
40
|
"aws-sdk-client-mock": "^4.1.0",
|
|
41
41
|
"aws-sdk-client-mock-jest": "^4.1.0",
|
|
42
42
|
"jest": "^30.1.3",
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
"publishConfig": {
|
|
69
69
|
"access": "public"
|
|
70
70
|
},
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "c063a2aaaaae0e4eaac6338f6adc250cf46b9103"
|
|
72
72
|
}
|