1
1
const fs = require ( 'fs' )
2
2
const ejs = require ( 'ejs' )
3
3
const path = require ( 'path' )
4
- const merge = require ( 'deepmerge' )
4
+ const deepmerge = require ( 'deepmerge' )
5
5
const resolve = require ( 'resolve' )
6
6
const { isBinaryFileSync } = require ( 'isbinaryfile' )
7
7
const mergeDeps = require ( './util/mergeDeps' )
@@ -14,6 +14,23 @@ const isString = val => typeof val === 'string'
14
14
const isFunction = val => typeof val === 'function'
15
15
const isObject = val => val && typeof val === 'object'
16
16
const mergeArrayWithDedupe = ( a , b ) => Array . from ( new Set ( [ ...a , ...b ] ) )
17
+ function pruneObject ( obj ) {
18
+ if ( typeof obj === 'object' ) {
19
+ for ( const k in obj ) {
20
+ if ( ! obj . hasOwnProperty ( k ) ) {
21
+ continue
22
+ }
23
+
24
+ if ( obj [ k ] == null ) {
25
+ delete obj [ k ]
26
+ } else {
27
+ obj [ k ] = pruneObject ( obj [ k ] )
28
+ }
29
+ }
30
+ }
31
+
32
+ return obj
33
+ }
17
34
18
35
class GeneratorAPI {
19
36
/**
@@ -176,15 +193,34 @@ class GeneratorAPI {
176
193
177
194
/**
178
195
* Extend the package.json of the project.
179
- * Nested fields are deep-merged unless `{ merge: false }` is passed.
180
196
* Also resolves dependency conflicts between plugins.
181
197
* Tool configuration fields may be extracted into standalone files before
182
198
* files are written to disk.
183
199
*
184
200
* @param {object | () => object } fields - Fields to merge.
185
- * @param {boolean } forceNewVersion - Ignore version conflicts when updating dependency version
201
+ * @param {object } [options] - Options for extending / merging fields.
202
+ * @param {boolean } [options.prune=false] - Remove null or undefined fields
203
+ * from the object after merging.
204
+ * @param {boolean } [options.merge=true] deep-merge nested fields, note
205
+ * that dependency fields are always deep merged regardless of this option.
206
+ * @param {boolean } [options.warnIncompatibleVersions=true] Output warning
207
+ * if two dependency version ranges don't intersect.
186
208
*/
187
- extendPackage ( fields , forceNewVersion ) {
209
+ extendPackage ( fields , options = { } ) {
210
+ const extendOptions = {
211
+ prune : false ,
212
+ merge : true ,
213
+ warnIncompatibleVersions : true
214
+ }
215
+
216
+ // this condition statement is added for compatiblity reason, because
217
+ // in version 4.0.0 to 4.1.2, there's no `options` object, but a `forceNewVersion` flag
218
+ if ( typeof options === 'boolean' ) {
219
+ extendOptions . warnIncompatibleVersions = ! options
220
+ } else {
221
+ Object . assign ( extendOptions , options )
222
+ }
223
+
188
224
const pkg = this . generator . pkg
189
225
const toMerge = isFunction ( fields ) ? fields ( pkg ) : fields
190
226
for ( const key in toMerge ) {
@@ -197,18 +233,22 @@ class GeneratorAPI {
197
233
existing || { } ,
198
234
value ,
199
235
this . generator . depSources ,
200
- forceNewVersion
236
+ extendOptions
201
237
)
202
- } else if ( ! ( key in pkg ) ) {
238
+ } else if ( ! extendOptions . merge || ! ( key in pkg ) ) {
203
239
pkg [ key ] = value
204
240
} else if ( Array . isArray ( value ) && Array . isArray ( existing ) ) {
205
241
pkg [ key ] = mergeArrayWithDedupe ( existing , value )
206
242
} else if ( isObject ( value ) && isObject ( existing ) ) {
207
- pkg [ key ] = merge ( existing , value , { arrayMerge : mergeArrayWithDedupe } )
243
+ pkg [ key ] = deepmerge ( existing , value , { arrayMerge : mergeArrayWithDedupe } )
208
244
} else {
209
245
pkg [ key ] = value
210
246
}
211
247
}
248
+
249
+ if ( extendOptions . prune ) {
250
+ pruneObject ( pkg )
251
+ }
212
252
}
213
253
214
254
/**
0 commit comments