/**
 * Copyright 2013-2026 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import assert from 'node:assert';
import fs, { existsSync, readFileSync, statSync } from 'node:fs';
import { rm } from 'node:fs/promises';
import path, { relative } from 'node:path';
import chalk from 'chalk';
import { execaCommandSync } from 'execa';
import { union } from 'lodash-es';
import semver, { lt as semverLessThan } from 'semver';
import { packageJson } from "../../lib/index.js";
import { packageNameToNamespace } from "../../lib/utils/index.js";
import CoreGenerator from "../base-core/index.js";
import { PRIORITY_NAMES } from "../base-core/priorities.js";
import { GENERATOR_JHIPSTER } from "../generator-constants.js";
import { mergeBlueprints, normalizeBlueprintName, parseBlueprints } from "./internal/index.js";
import { CONTEXT_DATA_BLUEPRINTS_TO_COMPOSE, CONTEXT_DATA_EXISTING_PROJECT, CONTEXT_DATA_REPRODUCIBLE_TIMESTAMP, LOCAL_BLUEPRINT_PACKAGE_NAMESPACE, formatDateForChangelog, } from "./support/index.js";
const { WRITING } = PRIORITY_NAMES;
/**
 * Base class that contains blueprints support.
 * Provides built-in state support with control object.
 */
export default class BaseGenerator extends CoreGenerator {
    fromBlueprint;
    sbsBlueprint;
    delegateToBlueprint = false;
    blueprintConfig;
    jhipsterContext;
    constructor(args, options, features) {
        const { jhipsterContext, ...opts } = options ?? {};
        super(args, opts, { blueprintSupport: true, ...features });
        if (this.options.help) {
            return;
        }
        const { sbsBlueprint = false, checkBlueprint, jhipsterBootstrap = this._namespace !== 'jhipster:project-name' &&
            !this._namespace.split(':')[1]?.startsWith('bootstrap') &&
            !this._namespace.endsWith(':bootstrap'), } = this.features;
        this.sbsBlueprint = sbsBlueprint;
        this.fromBlueprint = this.rootGeneratorName() !== 'generator-jhipster';
        if (this.fromBlueprint) {
            this.blueprintStorage = this._getStorage(undefined, { transform: this.features.configTransform });
            this.blueprintConfig = this.blueprintStorage.createProxy();
            // jhipsterContext is the original generator
            this.jhipsterContext = jhipsterContext;
            if (checkBlueprint && !this.jhipsterContext) {
                throw new Error(`This is a JHipster blueprint and should be used only like ${chalk.yellow(`jhipster --blueprints ${this.options.namespace.split(':')[0]}`)}`);
            }
            try {
                // Fallback to the original generator if the file does not exists in the blueprint.
                const blueprintedTemplatePath = this.jhipsterTemplatePath();
                if (!this.jhipsterTemplatesFolders.includes(blueprintedTemplatePath)) {
                    this.jhipsterTemplatesFolders.push(blueprintedTemplatePath);
                }
            }
            catch (error) {
                this.log.warn('Error adding current blueprint templates as alternative for JHipster templates.');
                this.log.log(error);
            }
        }
        if (jhipsterBootstrap) {
            // jhipster:bootstrap is always required. Run it once the environment starts.
            this.env.queueTask('environment:run', async () => this.composeWithJHipster('bootstrap').then(), {
                once: 'queueJhipsterBootstrap',
                startQueue: false,
            });
        }
        this.on('before:queueOwnTasks', () => {
            const { storeBlueprintVersion, storeJHipsterVersion, queueCommandTasks = true } = this.features;
            if (this.fromBlueprint && storeBlueprintVersion && !this.options.reproducibleTests) {
                try {
                    if (this.blueprintConfig.blueprintVersion) {
                        this.getContextData(this.#getBlueprintOldVersionKey(), { factory: () => this.blueprintConfig.blueprintVersion });
                    }
                    const blueprintPackageJson = JSON.parse(readFileSync(this._meta.packagePath, 'utf8'));
                    this.blueprintConfig.blueprintVersion = blueprintPackageJson.version;
                }
                catch {
                    this.log(`Could not retrieve version of blueprint '${this.options.namespace}'`);
                }
            }
            if (!this.fromBlueprint && !this.delegateToBlueprint && storeJHipsterVersion && !this.options.reproducibleTests) {
                this.jhipsterConfig.jhipsterVersion = packageJson.version;
            }
            if ((this.fromBlueprint || !this.delegateToBlueprint) && queueCommandTasks) {
                this._queueCurrentJHipsterCommandTasks();
            }
        });
    }
    #getBlueprintOldVersionKey() {
        return `oldVersion:${this.rootGeneratorName()}`;
    }
    #getBlueprintOldVersion() {
        try {
            return this.getContextData(this.#getBlueprintOldVersionKey());
        }
        catch {
            return undefined;
        }
    }
    /**
     * Filter generator's tasks in case the blueprint should be responsible on queueing those tasks.
     */
    delegateTasksToBlueprint(tasksGetter) {
        return this.delegateToBlueprint ? {} : tasksGetter();
    }
    get #control() {
        const generator = this;
        return this.getContextData('jhipster:control', {
            factory: () => {
                let jhipsterOldVersion;
                let environmentHasDockerCompose;
                const customizeRemoveFiles = [];
                const collectCleanupFiles = (cleanup) => Object.entries(cleanup).map(([version, files]) => {
                    const stringFiles = [];
                    for (const file of files) {
                        if (Array.isArray(file)) {
                            const [condition, ...fileParts] = file;
                            if (condition) {
                                stringFiles.push(...fileParts);
                            }
                        }
                        else {
                            stringFiles.push(file);
                        }
                    }
                    return {
                        removedInVersion: version,
                        files: stringFiles,
                    };
                });
                return {
                    get existingProject() {
                        try {
                            return generator.getContextData(CONTEXT_DATA_EXISTING_PROJECT);
                        }
                        catch {
                            return false;
                        }
                    },
                    get jhipsterOldVersion() {
                        jhipsterOldVersion ??=
                            existsSync(generator.config.path) ?
                                (JSON.parse(readFileSync(generator.config.path, 'utf-8'))[GENERATOR_JHIPSTER]?.jhipsterVersion ?? null)
                                : null;
                        return jhipsterOldVersion;
                    },
                    get environmentHasDockerCompose() {
                        if (environmentHasDockerCompose === undefined) {
                            const commandReturn = execaCommandSync('docker compose version', { reject: false, stdio: 'pipe' });
                            environmentHasDockerCompose = !commandReturn?.failed; // TODO looks to be a bug on ARM MaCs and execaCommandSync, does not return anything, assuming mac users are smart and install docker.
                        }
                        return environmentHasDockerCompose;
                    },
                    customizeRemoveFiles,
                    isJhipsterVersionLessThan(version) {
                        const { jhipsterOldVersion } = this;
                        return jhipsterOldVersion ? semverLessThan(jhipsterOldVersion, version) : false;
                    },
                    async removeFiles(assertions, ...files) {
                        const versions = typeof assertions === 'string' ? { removedInVersion: undefined, oldVersion: undefined } : assertions;
                        if (typeof assertions === 'string') {
                            files = [assertions, ...files];
                        }
                        for (const customize of this.customizeRemoveFiles) {
                            files = files.map(customize).filter(Boolean);
                        }
                        const { removedInVersion, oldVersion = this.jhipsterOldVersion } = versions;
                        if (removedInVersion && oldVersion && !semverLessThan(oldVersion, removedInVersion)) {
                            return;
                        }
                        const absolutePaths = files.map(file => generator.destinationPath(file));
                        // Delete from memory fs to keep updated.
                        generator.fs.delete(absolutePaths);
                        await Promise.all(absolutePaths.map(async (file) => {
                            const relativePath = relative(generator.env.logCwd, file);
                            try {
                                if (statSync(file).isFile()) {
                                    generator.log.info(`Removing legacy file ${relativePath}`);
                                    await rm(file, { force: true });
                                }
                            }
                            catch {
                                generator.log.info(`Could not remove legacy file ${relativePath}`);
                            }
                        }));
                    },
                    async cleanupFiles(oldVersionOrCleanup, cleanup) {
                        if (!this.jhipsterOldVersion)
                            return;
                        let oldVersion;
                        if (typeof oldVersionOrCleanup === 'string') {
                            oldVersion = oldVersionOrCleanup;
                            assert(cleanup, 'cleanupFiles requires cleanup object');
                        }
                        else {
                            cleanup = oldVersionOrCleanup;
                            oldVersion = this.jhipsterOldVersion;
                        }
                        await Promise.all(collectCleanupFiles(cleanup).map(async ({ removedInVersion, files }) => this.removeFiles({ oldVersion, removedInVersion }, ...files)));
                    },
                    async cleanupBlueprintFiles(cleanup) {
                        const oldVersion = generator.#getBlueprintOldVersion();
                        if (!oldVersion)
                            return;
                        await Promise.all(collectCleanupFiles(cleanup).map(async ({ removedInVersion, files }) => this.removeFiles({ oldVersion, removedInVersion }, ...files)));
                    },
                };
            },
        });
    }
    /**
     * Generate a timestamp to be used by Liquibase changelogs.
     */
    nextTimestamp() {
        const reproducible = Boolean(this.options.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();
        // Milliseconds is ignored for changelogDate.
        now.setMilliseconds(0);
        // Run reproducible timestamp when regenerating the project with reproducible option or a specific timestamp.
        if (reproducible || creationTimestamp) {
            now = this.getContextData(CONTEXT_DATA_REPRODUCIBLE_TIMESTAMP, {
                factory: () => {
                    const newCreationTimestamp = creationTimestamp ?? this.config.get('creationTimestamp');
                    const newDate = newCreationTimestamp ? new Date(newCreationTimestamp) : now;
                    newDate.setMilliseconds(0);
                    return newDate;
                },
            });
            now.setMinutes(now.getMinutes() + 1);
            this.getContextData(CONTEXT_DATA_REPRODUCIBLE_TIMESTAMP, { replacement: now });
            // Reproducible build can create future timestamp, save it.
            const { lastLiquibaseTimestamp } = this.jhipsterConfig;
            if (!lastLiquibaseTimestamp || now.getTime() > lastLiquibaseTimestamp) {
                this.config.set('lastLiquibaseTimestamp', now.getTime());
            }
        }
        else {
            // Get and store lastLiquibaseTimestamp, a future timestamp can be used
            const { lastLiquibaseTimestamp } = this.jhipsterConfig;
            if (lastLiquibaseTimestamp) {
                const lastTimestampDate = new Date(lastLiquibaseTimestamp);
                if (lastTimestampDate >= now) {
                    now = lastTimestampDate;
                    now.setSeconds(now.getSeconds() + 1);
                    now.setMilliseconds(0);
                }
            }
            this.jhipsterConfig.lastLiquibaseTimestamp = now.getTime();
        }
        return formatDateForChangelog(now);
    }
    /**
     * Get arguments for the priority
     */
    getArgsForPriority(priorityName) {
        const [firstArg] = super.getArgsForPriority(priorityName);
        const control = this.#control;
        if (priorityName === WRITING && existsSync(this.config.path)) {
            try {
                const oldConfig = JSON.parse(readFileSync(this.config.path).toString())[GENERATOR_JHIPSTER];
                const newConfig = this.config.getAll();
                const keys = [...new Set([...Object.keys(oldConfig), ...Object.keys(newConfig)])];
                const configChanges = Object.fromEntries(keys
                    .filter(key => Array.isArray(newConfig[key]) ?
                    newConfig[key].length === oldConfig[key].length &&
                        newConfig[key].find((element, index) => element !== oldConfig[key][index])
                    : newConfig[key] !== oldConfig[key])
                    .map(key => [key, { newValue: newConfig[key], oldValue: oldConfig[key] }]));
                return [{ ...firstArg, control, configChanges }];
            }
            catch {
                // Fail to parse
            }
        }
        return [{ ...firstArg, control }];
    }
    /**
     * Check if the generator should ask for prompts.
     */
    shouldAskForPrompts({ control }) {
        if (!control)
            throw new Error(`Control object not found in ${this.options.namespace}`);
        return !control.existingProject || this.options.askAnswered === true;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Initializing priority is used to show logo and tasks related to preparing for prompts, like loading constants.
     */
    get initializing() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asInitializingTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Prompting priority is used to prompt users for configuration values.
     */
    get prompting() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asPromptingTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Configuring priority is used to customize and validate the configuration.
     */
    get configuring() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asConfiguringTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Composing should be used to compose with others generators.
     */
    get composing() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asComposingTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * ComposingComponent priority should be used to handle component configuration order.
     */
    get composingComponent() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asComposingComponentTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Loading should be used to load application configuration from jhipster configuration.
     * Before this priority the configuration should be considered dirty, while each generator configures itself at configuring priority, another generator composed at composing priority can still change it.
     */
    get loading() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asLoadingTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Loading should be used to load application configuration from jhipster configuration.
     * Before this priority the configuration should be considered dirty, while each generator configures itself at configuring priority, another generator composed at composing priority can still change it.
     */
    get composingBootstrap() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asComposingBootstrapTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Preparing should be used to generate derived properties.
     */
    get preparing() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asPreparingTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Preparing should be used to generate derived properties.
     */
    get postPreparing() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asPostPreparingTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Default priority should used as misc customizations.
     */
    get default() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asDefaultTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Writing priority should used to write files.
     */
    get writing() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asWritingTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * PostWriting priority should used to customize files.
     */
    get postWriting() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asPostWritingTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * Install priority should used to prepare the project.
     */
    get install() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asInstallTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * PostWriting priority should used to customize files.
     */
    get postInstall() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asPostInstallTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Priority API stub for blueprints.
     *
     * End priority should used to say good bye and print instructions.
     */
    get end() {
        return {};
    }
    /**
     * Utility method to get typed objects for autocomplete.
     */
    asEndTaskGroup(taskGroup) {
        return taskGroup;
    }
    /**
     * Composes with blueprint generators, if any.
     * @protected
     */
    async composeWithBlueprints() {
        if (this.fromBlueprint) {
            throw new Error('Only the main generator can compose with blueprints');
        }
        const namespace = this._namespace;
        if (!namespace?.startsWith('jhipster:')) {
            throw new Error(`Generator is not blueprintable ${namespace}`);
        }
        const subGen = namespace.slice('jhipster:'.length);
        this.delegateToBlueprint = false;
        if (this.options.disableBlueprints) {
            return [];
        }
        let blueprints = await this.#configureBlueprints();
        if (this.options.composeWithLocalBlueprint) {
            blueprints = blueprints.concat('@jhipster/local');
        }
        const composedBlueprints = [];
        for (const blueprintName of blueprints) {
            const blueprintGenerator = await this.#composeBlueprint(blueprintName, subGen);
            let blueprintCommand;
            if (blueprintGenerator) {
                composedBlueprints.push(blueprintGenerator);
                if (blueprintGenerator.sbsBlueprint) {
                    // If sbsBlueprint, add templatePath to the original generator templatesFolder.
                    this.jhipsterTemplatesFolders.unshift(blueprintGenerator.templatePath());
                }
                else {
                    // If the blueprints does not sets sbsBlueprint property, ignore normal workflow.
                    this.delegateToBlueprint = true;
                    this.#checkBlueprintImplementsPriorities(blueprintGenerator);
                }
                const blueprintModule = await blueprintGenerator._meta?.importModule?.();
                blueprintCommand = blueprintModule?.command;
            }
            else {
                const generatorName = packageNameToNamespace(normalizeBlueprintName(blueprintName));
                const generatorNamespace = `${generatorName}:${subGen}`;
                const blueprintMeta = this.env.findMeta(generatorNamespace);
                const blueprintModule = await blueprintMeta?.importModule?.();
                blueprintCommand = blueprintModule?.command;
                if (blueprintCommand?.compose) {
                    this.generatorsToCompose.push(...blueprintCommand.compose);
                }
            }
            if (blueprintCommand?.override) {
                if (this.generatorCommand) {
                    this.log.warn('Command already set, multiple blueprints may be overriding the command. Unexpected behavior may occur.');
                }
                // Use the blueprint command if it is set to override.
                this.generatorCommand = blueprintCommand;
            }
        }
        return composedBlueprints;
    }
    /**
     * Check if the blueprint implements every priority implemented by the parent generator
     * @param {BaseGenerator} blueprintGenerator
     */
    #checkBlueprintImplementsPriorities(blueprintGenerator) {
        const { taskPrefix: baseGeneratorTaskPrefix = '' } = this.features;
        const { taskPrefix: blueprintTaskPrefix = '' } = blueprintGenerator.features;
        // v8 remove deprecated priorities
        const DEPRECATED_PRIORITIES = new Set(['preConflicts']);
        for (const priorityName of Object.values(PRIORITY_NAMES).filter(p => !DEPRECATED_PRIORITIES.has(p))) {
            const baseGeneratorPriorityName = `${baseGeneratorTaskPrefix}${priorityName}`;
            if (baseGeneratorPriorityName in this) {
                const blueprintPriorityName = `${blueprintTaskPrefix}${priorityName}`;
                if (!Object.hasOwn(Object.getPrototypeOf(blueprintGenerator), blueprintPriorityName)) {
                    this.log.debug(`Priority ${blueprintPriorityName} not implemented at ${blueprintGenerator.options.namespace}.`);
                }
            }
        }
    }
    /**
     * Configure blueprints.
     * @private
     */
    async #configureBlueprints() {
        try {
            return this.getContextData(CONTEXT_DATA_BLUEPRINTS_TO_COMPOSE);
        }
        catch {
            // Ignore
        }
        let argvBlueprints = this.options.blueprints || '';
        // check for old single blueprint declaration
        let { blueprint } = this.options;
        if (blueprint) {
            if (typeof blueprint === 'string') {
                blueprint = [blueprint];
            }
            this.log.warn('--blueprint option is deprecated. Please use --blueprints instead');
            argvBlueprints = union(blueprint, argvBlueprints.split(',')).join(',');
        }
        const blueprints = mergeBlueprints(parseBlueprints(argvBlueprints), this.jhipsterConfig.blueprints ?? []);
        // EnvironmentBuilder already looks for blueprint when running from cli, this is required for tests.
        // Can be removed once the tests uses EnvironmentBuilder.
        const missingBlueprints = blueprints
            .filter(blueprint => !this.env.isPackageRegistered(packageNameToNamespace(blueprint.name)))
            .map(blueprint => blueprint.name);
        if (missingBlueprints.length > 0) {
            await this.env.lookup({ filterPaths: true, packagePatterns: missingBlueprints });
        }
        if (blueprints && blueprints.length > 0) {
            blueprints.forEach(blueprint => {
                blueprint.version = this.#findBlueprintVersion(blueprint.name) ?? blueprint.version;
            });
            this.jhipsterConfig.blueprints = blueprints;
        }
        if (!this.skipChecks) {
            const namespaces = blueprints.map(blueprint => packageNameToNamespace(blueprint.name));
            // Verify if the blueprints have been registered.
            const missing = namespaces.filter(namespace => !this.env.isPackageRegistered(namespace));
            if (missing && missing.length > 0) {
                throw new Error(`Some blueprints were not found ${missing}, you should install them manually`);
            }
            blueprints.forEach(blueprint => {
                this.#checkJHipsterBlueprintVersion(blueprint.name);
            });
        }
        const blueprintNames = blueprints.map(blueprint => blueprint.name);
        this.getContextData(CONTEXT_DATA_BLUEPRINTS_TO_COMPOSE, {
            replacement: blueprintNames,
        });
        return blueprintNames;
    }
    /**
     * Compose external blueprint module
     */
    async #composeBlueprint(blueprint, subGen) {
        blueprint = normalizeBlueprintName(blueprint);
        if (!this.skipChecks && blueprint !== LOCAL_BLUEPRINT_PACKAGE_NAMESPACE) {
            this.#checkBlueprint(blueprint);
        }
        const generatorName = packageNameToNamespace(blueprint);
        const generatorNamespace = `${generatorName}:${subGen}`;
        if (!(await this.env.get(generatorNamespace))) {
            this.log.debug(`No blueprint found for blueprint ${chalk.yellow(blueprint)} and ${chalk.yellow(subGen)} with namespace ${chalk.yellow(generatorNamespace)} subgenerator: falling back to default generator`);
            return undefined;
        }
        this.log.debug(`Found blueprint ${chalk.yellow(blueprint)} and ${chalk.yellow(subGen)} with namespace ${chalk.yellow(generatorNamespace)}`);
        const blueprintGenerator = await this.composeWith(generatorNamespace, {
            forwardOptions: true,
            schedule: generator => generator.sbsBlueprint,
            generatorArgs: this._args,
            generatorOptions: {
                jhipsterContext: this,
            },
        });
        if (blueprintGenerator instanceof Error) {
            throw blueprintGenerator;
        }
        this._debug(`Using blueprint ${chalk.yellow(blueprint)} for ${chalk.yellow(subGen)} subgenerator`);
        return blueprintGenerator;
    }
    /**
     * Try to retrieve the package.json of the blueprint used, as an object.
     * @private
     * @param {string} blueprintPkgName - generator name
     * @return {object} packageJson - retrieved package.json as an object or undefined if not found
     */
    #findBlueprintPackageJson(blueprintPkgName) {
        const blueprintGeneratorName = packageNameToNamespace(blueprintPkgName);
        const blueprintPackagePath = this.env.getPackagePath(blueprintGeneratorName);
        if (!blueprintPackagePath) {
            this.log.warn(`Could not retrieve packagePath of blueprint '${blueprintPkgName}'`);
            return undefined;
        }
        const packageJsonFile = path.join(blueprintPackagePath, 'package.json');
        if (!fs.existsSync(packageJsonFile)) {
            return undefined;
        }
        return JSON.parse(fs.readFileSync(packageJsonFile).toString());
    }
    /**
     * Try to retrieve the version of the blueprint used.
     * @private
     * @param {string} blueprintPkgName - generator name
     * @return {string} version - retrieved version or empty string if not found
     */
    #findBlueprintVersion(blueprintPkgName) {
        const blueprintPackageJson = this.#findBlueprintPackageJson(blueprintPkgName);
        if (!blueprintPackageJson?.version) {
            this.log.warn(`Could not retrieve version of blueprint '${blueprintPkgName}'`);
            return undefined;
        }
        return blueprintPackageJson.version;
    }
    /**
     * Check if the generator specified as blueprint is installed.
     */
    #checkBlueprint(blueprint) {
        if (blueprint === 'generator-jhipster') {
            throw new Error(`You cannot use ${chalk.yellow(blueprint)} as the blueprint.`);
        }
    }
    /**
     * Check if the generator specified as blueprint has a version compatible with current JHipster.
     */
    #checkJHipsterBlueprintVersion(blueprintPkgName) {
        const blueprintPackageJson = this.#findBlueprintPackageJson(blueprintPkgName);
        if (!blueprintPackageJson) {
            this.log.warn(`Could not retrieve version of JHipster declared by blueprint '${blueprintPkgName}'`);
            return;
        }
        const mainGeneratorJhipsterVersion = packageJson.version;
        const compatibleJhipsterRange = blueprintPackageJson.engines?.['generator-jhipster'] ??
            blueprintPackageJson.dependencies?.['generator-jhipster'] ??
            blueprintPackageJson.peerDependencies?.['generator-jhipster'];
        if (compatibleJhipsterRange) {
            if (!semver.valid(compatibleJhipsterRange) && !semver.validRange(compatibleJhipsterRange)) {
                this.log.verboseInfo(`Blueprint ${blueprintPkgName} contains generator-jhipster dependency with non comparable version`);
                return;
            }
            if (semver.satisfies(mainGeneratorJhipsterVersion, compatibleJhipsterRange, { includePrerelease: true })) {
                return;
            }
            throw new Error(`The installed ${chalk.yellow(blueprintPkgName)} blueprint targets JHipster v${compatibleJhipsterRange} and is not compatible with this JHipster version. Either update the blueprint or JHipster. You can also disable this check using --skip-checks at your own risk`);
        }
        this.log.warn(`Could not retrieve version of JHipster declared by blueprint '${blueprintPkgName}'`);
    }
}
export class CommandBaseGenerator extends BaseGenerator {
}
