diff --git a/src/actions/lifecycle.ts b/src/actions/lifecycle.ts index 8ddecb7e..37091a7c 100644 --- a/src/actions/lifecycle.ts +++ b/src/actions/lifecycle.ts @@ -141,8 +141,24 @@ export abstract class TasksMixin { } } - getTaskSources(): any[] { - return [Object.getPrototypeOf(this)]; + /** + * Get task sources property descriptors. + */ + getTaskSourcesPropertyDescriptors(this: BaseGeneratorImpl): any { + if (this.features.inheritTasks) { + const queueNames = Object.keys(this._queues); + let currentPrototype = Object.getPrototypeOf(this); + let propertyDescriptors = []; + while (currentPrototype) { + propertyDescriptors.unshift(...Object.entries(Object.getOwnPropertyDescriptors(currentPrototype))); + currentPrototype = Object.getPrototypeOf(currentPrototype); + } + + propertyDescriptors = propertyDescriptors.filter(([name]) => queueNames.includes(name)); + return Object.fromEntries(propertyDescriptors); + } + + return Object.getOwnPropertyDescriptors(Object.getPrototypeOf(this)); } /** @@ -167,7 +183,7 @@ export abstract class TasksMixin { const propertyName = `${taskPrefix}${name}`; const property = taskOptions.taskOrigin ? Object.getOwnPropertyDescriptor(taskOptions.taskOrigin, propertyName) - : this.getTaskSources().find(taskSource => Object.getOwnPropertyDescriptor(taskSource, propertyName)); + : this.getTaskSourcesPropertyDescriptors()[propertyName]; if (!property) return []; @@ -222,7 +238,7 @@ export abstract class TasksMixin { * Get task names. */ getTaskNames(this: BaseGeneratorImpl): string[] { - const methods = [...new Set(this.getTaskSources().flatMap(taskSource => Object.getOwnPropertyNames(taskSource)))]; + const methods = Object.keys(this.getTaskSourcesPropertyDescriptors()); let validMethods = methods.filter(method => methodIsValid(method)); const { taskPrefix } = this.features; diff --git a/src/types.d.ts b/src/types.d.ts index 623095a4..c4cadd54 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -100,6 +100,9 @@ export type BaseFeatures = FeaturesApi & { /** Custom priorities for more fine tuned workflows. */ customPriorities?: Priority[]; + + /** Inherit tasks from parent prototypes, implies tasksMatchingPriority */ + inheritTasks?: boolean; }; export type BaseOptions = OptionsApi & { diff --git a/test/base.test.ts b/test/base.test.ts index e02cf964..da56cddb 100644 --- a/test/base.test.ts +++ b/test/base.test.ts @@ -1889,18 +1889,21 @@ describe('Base', () => { describe('getTaskNames', () => { class TestGen extends Base { constructor(args, options, features) { + const customPriorities = [{ priorityName: 'customPriority', before: 'prompting' }]; super( args, - { - ...options, - customPriorities: [ - { - priorityName: 'customPriority', - before: 'prompting', + Array.isArray(args) + ? options + : { + ...options, + customPriorities, }, - ], - }, - features, + Array.isArray(args) + ? { + ...features, + customPriorities, + } + : features, ); } @@ -1920,12 +1923,48 @@ describe('Base', () => { assert.deepStrictEqual(gen.getTaskNames(), ['anyMethod', 'default', 'customPriority']); }); - it('should return any public member when tasksMatchingPriority is true', async function () { - const Gen = helpers.createDummyGenerator(TestGen, { + it('should return any public member when tasksMatchingPriority is false', async function () { + const Gen = helpers.createDummyGenerator(class extends TestGen {}, { default() {}, customPriority() {}, + otherMethod() {}, }); - assert.deepStrictEqual(new Gen({ env }).getTaskNames(), ['default', 'customPriority']); + assert.deepStrictEqual(new Gen({ env }).getTaskNames(), ['default', 'customPriority', 'otherMethod']); + }); + + it('should return only priorities tasks when tasksMatchingPriority is true', async function () { + const Gen = class extends TestGen { + constructor(args, options, features) { + super(args, options, { ...features, tasksMatchingPriority: true }); + } + + default() {} + + customPriority() {} + + otherMethod() {} + }; + + assert.deepStrictEqual(new Gen([], { env }).getTaskNames(), ['default', 'customPriority']); + }); + + it('should return only inherited tasks when inheritTasks is true', async function () { + const Gen = class extends TestGen { + constructor(args, options, features) { + super(args, options, { ...features, inheritTasks: true }); + } + + default() {} + + initializing() {} + }; + + const gen = new Gen([], { env }); + assert.deepStrictEqual(gen.getTaskNames(), ['default', 'customPriority', 'initializing']); + assert.strictEqual( + gen.getTaskSourcesPropertyDescriptors().default.value, + Object.getOwnPropertyDescriptor(Object.getPrototypeOf(gen), 'default')!.value, + ); }); }); });