diff --git a/packages/tao/src/shared/params.spec.ts b/packages/tao/src/shared/params.spec.ts index e581db8eb9..5ff25a197b 100644 --- a/packages/tao/src/shared/params.spec.ts +++ b/packages/tao/src/shared/params.spec.ts @@ -565,6 +565,85 @@ describe('params', () => { expect(opts).toEqual({ a: [] }); }); + it('should set the default object value', () => { + const opts = setDefaults( + { + a: { + key: 'value', + }, + }, + { + properties: { + a: { + type: 'object', + properties: { + key: { + type: 'string', + }, + key2: { + type: 'string', + default: 'value2', + }, + }, + }, + }, + } + ); + + expect(opts).toEqual({ a: { key: 'value', key2: 'value2' } }); + }); + + it('should not default object properties to {}', () => { + const opts = setDefaults( + {}, + { + properties: { + a: { + type: 'object', + properties: { + key: { + type: 'string', + }, + }, + }, + }, + } + ); + + expect(opts).toEqual({}); + }); + + it('should be able to set defaults for underlying properties', () => { + const opts = setDefaults( + {}, + { + properties: { + a: { + type: 'object', + properties: { + minify: { + type: 'boolean', + default: true, + }, + inlineCritical: { + type: 'boolean', + default: true, + }, + }, + additionalProperties: false, + }, + }, + } + ); + + expect(opts).toEqual({ + a: { + minify: true, + inlineCritical: true, + }, + }); + }); + it('should resolve types using refs', () => { const opts = setDefaults( {}, diff --git a/packages/tao/src/shared/params.ts b/packages/tao/src/shared/params.ts index ed91b6caea..793d2bb6c1 100644 --- a/packages/tao/src/shared/params.ts +++ b/packages/tao/src/shared/params.ts @@ -16,7 +16,12 @@ type PropertyDescription = { description?: string; format?: string; visible?: boolean; - default?: string | number | boolean | string[]; + default?: + | string + | number + | boolean + | string[] + | { [key: string]: string | number | boolean | string[] }; $ref?: string; $default?: { $source: 'argv'; index: number } | { $source: 'projectName' }; additionalProperties?: boolean; @@ -436,10 +441,18 @@ function setPropertyDefault( opts[propName] = schema.default; } } else { - if (!opts[propName]) { + const wasUndefined = opts[propName] === undefined; + if (wasUndefined) { + // We need an object to set values onto opts[propName] = {}; } + setDefaultsInObject(opts[propName], schema.properties || {}, definitions); + + // If the property was initially undefined but no properties were added, we remove it again instead of having an {} + if (wasUndefined && Object.keys(opts[propName]).length === 0) { + delete opts[propName]; + } } } diff --git a/packages/workspace/src/core/project-graph/operators.ts b/packages/workspace/src/core/project-graph/operators.ts index b85ee5af43..1cd52ec1c7 100644 --- a/packages/workspace/src/core/project-graph/operators.ts +++ b/packages/workspace/src/core/project-graph/operators.ts @@ -55,7 +55,7 @@ export function isWorkspaceProject(project: ProjectGraphNode) { export function isNpmProject( project: ProjectGraphNode ): project is ProjectGraphNode<{ packageName: string; version: string }> { - return project.type === 'npm'; + return project?.type === 'npm'; } export function getSortedProjectNodes(nodes: Record) {