Skip to content

Commit

Permalink
Merge pull request #27133 from mshima/command-type
Browse files Browse the repository at this point in the history
export types from command
  • Loading branch information
DanielFran authored Aug 31, 2024
2 parents d21723a + 5546d54 commit 09db3d8
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 65 deletions.
6 changes: 3 additions & 3 deletions generators/base-core/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -598,12 +598,12 @@ You can ignore this error by passing '--skip-checks' to jhipster command.`);
return Object.entries(configs)
.filter(([_name, def]) => def?.prompt)
.map(([name, def]) => {
const promptSpec = typeof def.prompt === 'function' ? def.prompt(this as any, def) : { ...def.prompt };
let promptSpec = typeof def.prompt === 'function' ? def.prompt(this as any, def) : { ...def.prompt };
let storage: any;
if ((def.scope ?? 'storage') === 'storage') {
storage = this.config;
if (promptSpec.default === undefined) {
promptSpec.default = () => (this as any).jhipsterConfigWithDefaults?.[name];
promptSpec = { ...promptSpec, default: () => (this as any).jhipsterConfigWithDefaults?.[name] };
}
} else if (def.scope === 'blueprint') {
storage = this.blueprintStorage;
Expand Down Expand Up @@ -631,7 +631,7 @@ You can ignore this error by passing '--skip-checks' to jhipster command.`);
*/
dateFormatForLiquibase(reproducible?: boolean) {
const control = this.sharedData.getControl();
reproducible = reproducible ?? control.reproducible;
reproducible = reproducible ?? Boolean(control.reproducible);
// Use started counter or use stored creationTimestamp if creationTimestamp option is passed
const creationTimestamp = this.options.creationTimestamp ? this.config.get('creationTimestamp') : undefined;
let now = new Date();
Expand Down
111 changes: 82 additions & 29 deletions generators/base/api.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { ArgumentSpec, BaseFeatures, BaseOptions, CliOptionSpec } from 'yeoman-generator';
import type { RequireAtLeastOne, SetOptional } from 'type-fest';
import type { RequireAtLeastOne, SetOptional, TaggedUnion } from 'type-fest';
import type CoreGenerator from '../base-core/index.js';

type ConfigScope = 'storage' | 'blueprint' | 'control' | 'generator';
type CliSpecType = CliOptionSpec['type'];

export type ApplicationWithConfig = {
config: Record<string, string | boolean | number | string[]>;
entities: Record<string, unknown>;
Expand Down Expand Up @@ -192,13 +195,13 @@ export type WriteFileOptions<Generator = CoreGenerator, DataType = any> = {
}
);

export type JHispterChoices = string[] | { value: string; name: string }[];
export type JHispterChoices = readonly string[] | readonly { value: string; name: string }[];

export type JHipsterOption = SetOptional<CliOptionSpec, 'name'> & {
name?: string;
scope?: 'storage' | 'blueprint' | 'control' | 'generator';
env?: string;
choices?: JHispterChoices;
readonly name?: string;
readonly scope?: 'storage' | 'blueprint' | 'control' | 'generator';
readonly env?: string;
readonly choices?: JHispterChoices;
};

export type ValidationResult = {
Expand All @@ -209,25 +212,27 @@ export type ValidationResult = {
};

export type PromptSpec = {
type: 'input' | 'list' | 'confirm' | 'checkbox';
message: string | ((any) => string);
when?: boolean | ((any) => boolean);
default?: any | ((any) => any);
filter?: any | ((any) => any);
transformer?: any | ((any) => any);
validate?: any | ((any) => any);
readonly type: 'input' | 'list' | 'confirm' | 'checkbox';
readonly message: string | ((any) => string);
readonly when?: boolean | ((any) => boolean);
readonly default?: any | ((any) => any);
readonly filter?: any | ((any) => any);
readonly transformer?: any | ((any) => any);
readonly validate?: any | ((any) => any);
};

export type JHipsterArgumentConfig = SetOptional<ArgumentSpec, 'name'> & { scope?: 'storage' | 'blueprint' | 'generator' };

export type ConfigSpec = {
description?: string;
choices?: JHispterChoices;

cli?: SetOptional<CliOptionSpec, 'name'> & { env?: string };
argument?: JHipsterArgumentConfig;
prompt?: PromptSpec | ((gen: CoreGenerator & { jhipsterConfigWithDefaults: Record<string, any> }, config: ConfigSpec) => PromptSpec);
scope?: 'storage' | 'blueprint' | 'generator';
readonly description?: string;
readonly choices?: JHispterChoices;

readonly cli?: SetOptional<CliOptionSpec, 'name'> & { env?: string };
readonly argument?: JHipsterArgumentConfig;
readonly prompt?:
| PromptSpec
| ((gen: CoreGenerator & { jhipsterConfigWithDefaults: Record<string, any> }, config: ConfigSpec) => PromptSpec);
readonly scope?: 'storage' | 'blueprint' | 'generator';
/**
* The callback receives the generator as input for 'generator' scope.
* The callback receives jhipsterConfigWithDefaults as input for 'storage' (default) scope.
Expand All @@ -236,11 +241,16 @@ export type ConfigSpec = {
* Default value will not be applied to generator (using 'generator' scope) in initializing priority. Use cli.default instead.
* Default value will be application to templates context object (application) in loading priority.
*/
default?: string | boolean | number | string[] | ((this: CoreGenerator | void, ctx: any) => string | boolean | number | string[]);
readonly default?:
| string
| boolean
| number
| readonly string[]
| ((this: CoreGenerator | void, ctx: any) => string | boolean | number | readonly string[]);
/**
* Configure the generator according to the selected configuration.
*/
configure?: (gen: CoreGenerator) => void;
readonly configure?: (gen: CoreGenerator) => void;
};

export type JHipsterArguments = Record<string, JHipsterArgumentConfig>;
Expand All @@ -250,26 +260,69 @@ export type JHipsterOptions = Record<string, JHipsterOption>;
export type JHipsterConfigs = Record<string, RequireAtLeastOne<ConfigSpec, 'argument' | 'cli' | 'prompt'>>;

export type JHipsterCommandDefinition = {
arguments?: JHipsterArguments;
options?: JHipsterOptions;
configs?: JHipsterConfigs;
readonly arguments?: JHipsterArguments;
readonly options?: JHipsterOptions;
readonly configs?: JHipsterConfigs;
/**
* Import options from a generator.
* @example ['server', 'jhipster-blueprint:server']
*/
import?: string[];
readonly import?: readonly string[];
/**
* @experimental
* Compose with generator.
* @example ['server', 'jhipster-blueprint:server']
*/
compose?: string[];
readonly compose?: readonly string[];
/**
* Override options from the generator been blueprinted.
*/
override?: boolean;
readonly override?: boolean;
/**
* Load old options definition (yeoman's `this.options()`) from the generator.
*/
loadGeneratorOptions?: boolean;
readonly loadGeneratorOptions?: boolean;
};

/**
* A simplified version of the `JHipsterCommandDefinition` type for types parsing.
*/
type ParseableConfig = {
type?: CliSpecType;
cli?: {
type: CliSpecType;
};
scope: ConfigScope;
};
type ParseableCommand = {
readonly options?: Record<any, ParseableConfig>;
readonly configs?: Record<any, ParseableConfig>;
};

/** Extract contructor return type, eg: Boolean, String */
type ConstructorReturn<T> = T extends new () => infer R ? R : any;
type FilteredConfigScope = ConfigScope | undefined;
/** Add name to Options/Configs */
type TaggedParseableConfigUnion<D> = D extends Record<string, any> ? TaggedUnion<'name', D> : never;
/** Get union of Options and Configs */
type CommandUnion<C extends ParseableCommand> = TaggedParseableConfigUnion<C['configs']> | TaggedParseableConfigUnion<C['options']>;
type GetType<C extends ParseableConfig> =
C extends Record<'type', CliSpecType> ? C['type'] : C extends Record<'cli', Record<'type', CliSpecType>> ? C['cli']['type'] : never;
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
type WrapperToPrimitive<T> = T extends boolean ? Boolean : T extends String ? string : T extends Number ? number : T;

type UnionToObject<U extends { name: string; scope: ConfigScope }> = {
[K in U as K['name']]?: WrapperToPrimitive<ConstructorReturn<GetType<K>>>;
};

/** Filter Options/Config by scope */
type FilterScope<D, S extends FilteredConfigScope> =
D extends Record<'scope', S> ? D : D extends Record<'scope', ConfigScope> ? never : S extends undefined ? D : never;

export type ExportStoragePropertiesFromCommand<C extends ParseableCommand> = UnionToObject<FilterScope<CommandUnion<C>, 'storage'>>;

export type ExportGeneratorPropertiesFromCommand<C extends ParseableCommand> = UnionToObject<FilterScope<CommandUnion<C>, 'generator'>>;

export type ExportControlPropertiesFromCommand<C extends ParseableCommand> = UnionToObject<FilterScope<CommandUnion<C>, 'control'>>;

export type ExportBlueprintPropertiesFromCommand<C extends ParseableCommand> = UnionToObject<FilterScope<CommandUnion<C>, 'blueprint'>>;
12 changes: 8 additions & 4 deletions generators/base/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { JHipsterCommandDefinition } from '../base/api.js';
import { asCommand } from '../type-utils.js';

const command: JHipsterCommandDefinition = {
const command = {
options: {
useVersionPlaceholders: {
description: 'replace mutable versions with placeholders',
Expand All @@ -41,6 +41,7 @@ const command: JHipsterCommandDefinition = {
disableBlueprints: {
description: 'Disable blueprints support',
type: Boolean,
scope: 'generator',
},
debugEnabled: {
name: 'debug',
Expand All @@ -57,6 +58,7 @@ const command: JHipsterCommandDefinition = {
skipPrompts: {
description: 'Skip prompts',
type: Boolean,
scope: 'generator',
},
ignoreNeedlesError: {
description: 'Ignore needles failures',
Expand All @@ -65,6 +67,8 @@ const command: JHipsterCommandDefinition = {
scope: 'generator',
},
},
};
} as const;

export type Command = typeof command;

export default command;
export default asCommand(command);
7 changes: 5 additions & 2 deletions generators/base/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import type { Entity } from '../base-application/index.ts';
import { ExportControlPropertiesFromCommand } from './api.js';
import type { Command } from './command.ts';

export type Control = {
type BaseApplicationControlProperties = ExportControlPropertiesFromCommand<Command>;

export type Control = BaseApplicationControlProperties & {
existingProject: boolean;
ignoreNeedlesError: boolean;
jhipsterOldVersion: string | null;
useVersionPlaceholders?: boolean;
reproducible?: boolean;
/**
* Configure blueprints once per application.
*/
Expand Down
14 changes: 11 additions & 3 deletions generators/client/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const promptValueToMicrofrontends = answer =>
.map(baseName => ({ baseName }))
: [];

const command: JHipsterCommandDefinition = {
const command = {
options: {},
configs: {
clientFramework: {
Expand All @@ -57,6 +57,7 @@ const command: JHipsterCommandDefinition = {
{ value: VUE, name: 'Vue' },
{ value: CLIENT_FRAMEWORK_NO, name: 'No client' },
],
scope: 'storage',
},
microfrontend: {
description: 'Enable microfrontend support',
Expand All @@ -71,6 +72,7 @@ const command: JHipsterCommandDefinition = {
message: `Do you want to enable ${chalk.yellow('*microfrontends*')}?`,
default: false,
}),
scope: 'storage',
},
microfrontends: {
description: 'Microfrontends to load',
Expand All @@ -95,6 +97,7 @@ const command: JHipsterCommandDefinition = {
filter: promptValueToMicrofrontends,
transformer: microfrontendsToPromptValue,
}),
scope: 'storage',
},
clientTestFrameworks: {
description: 'Client test frameworks',
Expand All @@ -105,6 +108,7 @@ const command: JHipsterCommandDefinition = {
default: () => intersection([CYPRESS], config.testFrameworks),
}),
choices: [{ name: 'Cypress', value: CYPRESS }],
scope: 'storage',
},
withAdminUi: {
description: 'Generate administrative user interface',
Expand All @@ -116,15 +120,19 @@ const command: JHipsterCommandDefinition = {
when: answers => [ANGULAR, REACT, VUE].includes(answers.clientFramework ?? config.clientFramework),
message: 'Do you want to generate the admin UI?',
}),
scope: 'storage',
},
clientRootDir: {
description: 'Client root',
cli: {
type: String,
},
scope: 'storage',
},
},
import: [GENERATOR_COMMON],
};
} as const;

export default command;
export type Command = typeof command;

export default command as JHipsterCommandDefinition;
8 changes: 6 additions & 2 deletions generators/client/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import type { addIconImport, addItemToMenu, addRoute } from '../angular/support/needles.js';
import type { AngularApplication } from '../angular/types.js';
import type { OptionWithDerivedProperties } from '../base-application/application-options.js';
import type { ExportStoragePropertiesFromCommand } from '../base/api.js';
import type { CypressApplication } from '../cypress/types.js';
import type { JavaScriptApplication, JavaScriptSourceType } from '../javascript/types.js';
import type { Command } from './command.ts';

type ClientFrameworkType = ['no', 'angular', 'react', 'vue', 'svelte'];

type ClientFrameworkApplication = OptionWithDerivedProperties<'clientFramework', ClientFrameworkType>;

export type ClientApplication = ClientFrameworkApplication &
type ApplicationClientProperties = ExportStoragePropertiesFromCommand<Command>;

export type ClientApplication = ApplicationClientProperties &
ClientFrameworkApplication &
JavaScriptApplication &
AngularApplication &
CypressApplication & {
withAdminUi: boolean;
webappLoginRegExp: string;
webappEnumerationsDir?: string;
};
Expand Down
2 changes: 2 additions & 0 deletions generators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export {
CLIENT_TEST_SRC_DIR as TEMPLATES_JAVASCRIPT_TEST_DIR,
} from './generator-constants.js';

export * from './type-utils.js';

export type { JHipsterCommandDefinition } from './base/api.js';

export { default as GeneratorBase } from './base/index.js';
Expand Down
8 changes: 5 additions & 3 deletions generators/java/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { JHipsterCommandDefinition } from '../base/api.js';
import { asCommand } from '../type-utils.js';

const command: JHipsterCommandDefinition = {
const command = {
options: {},
import: ['jhipster:java:bootstrap', 'jhipster:java:domain', 'jhipster:java:build-tool'],
};

export default command;
export type Coomand = typeof command;

export default asCommand(command);
13 changes: 8 additions & 5 deletions generators/java/generators/bootstrap/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { JHipsterCommandDefinition } from '../../../base/api.js';
import { PromptSpec } from '../../../base/api.js';
import { asCommand } from '../../../type-utils.js';

const command: JHipsterCommandDefinition = {
const command = {
options: {
withGeneratedFlag: {
description: 'Add a GeneratedByJHipster annotation to all generated java classes and interfaces',
Expand All @@ -38,7 +39,7 @@ const command: JHipsterCommandDefinition = {
cli: {
type: String,
},
prompt: gen => ({
prompt: (gen): PromptSpec => ({
type: 'input',
message: 'What is your default Java package name?',
default: gen.jhipsterConfigWithDefaults.packageName,
Expand All @@ -52,6 +53,8 @@ const command: JHipsterCommandDefinition = {
},
},
import: [],
};
} as const;

export default command;
export type Command = typeof command;

export default asCommand(command);
Loading

0 comments on commit 09db3d8

Please sign in to comment.