UNPKG

gen-jhipster

Version:

Spring Boot + Angular/React/Vue in one handy generator

373 lines (372 loc) 18.4 kB
/** * Copyright 2013-2024 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 chalk from 'chalk'; import { padEnd, startCase } from 'lodash-es'; import BaseApplicationGenerator from '../base-application/index.js'; import { updateLanguagesTask as updateLanguagesInAngularTask } from '../angular/support/index.js'; import { updateLanguagesTask as updateLanguagesInReact } from '../react/support/index.js'; import { updateLanguagesTask as updateLanguagesInVue } from '../vue/support/index.js'; import { updateLanguagesTask as updateLanguagesInJava } from '../server/support/index.js'; import { SERVER_MAIN_RES_DIR, SERVER_TEST_RES_DIR } from '../generator-constants.js'; import { QUEUES } from '../base-application/priorities.js'; import { PRIORITY_NAMES } from '../base/priorities.js'; import { clientFrameworkTypes } from '../../lib/jhipster/index.js'; import { findLanguageForTag, supportedLanguages } from './support/languages.js'; import TranslationData, { createTranslationsFileFilter, createTranslationsFilter } from './translation-data.js'; import { writeEntityFiles } from './entity-files.js'; import { clientI18nFiles } from './files.js'; import { askForLanguages, askI18n } from './prompts.js'; const { NO: NO_CLIENT_FRAMEWORK, ANGULAR } = clientFrameworkTypes; /** * This is the base class for a generator that generates entities. */ export default class LanguagesGenerator extends BaseApplicationGenerator { askForMoreLanguages; askForNativeLanguage; translationData; supportedLanguages; languages; /** * Languages to be generated. * Can be incremental or every language. */ languagesToApply; composedBlueprints = []; languageCommand; writeJavaLanguageFiles; regenerateLanguages; constructor(args, options, features) { super(args, options, features); this.languageCommand = this.options.commandName === 'languages'; } async beforeQueue() { this.supportedLanguages = supportedLanguages; if (!this.fromBlueprint) { this.composedBlueprints = await this.composeWithBlueprints(); } if (!this.delegateToBlueprint) { await this.dependsOnBootstrapApplication(); } if (!this.jhipsterConfigWithDefaults.skipClient && this.jhipsterConfigWithDefaults.clientFramework !== NO_CLIENT_FRAMEWORK && (!this.jhipsterConfig.enableTranslation || this.jhipsterConfigWithDefaults.clientFramework === ANGULAR)) { // We must write languages files for translation process for entities only generation. // Angular frontend uses translation files even if enableTranslation is enabled. // As side effect, with angular frontends, translation files will be written for nativeLanguage for entity only generation. this.setFeatures({ disableSkipPriorities: true }); } } // Public API method used by the getter and also by Blueprints get initializing() { return this.asInitializingTaskGroup({ languagesToApply() { // Validate languages passed as argument. // Additional languages, will not replace current ones. this.languagesToApply = [this.options.nativeLanguage, ...(this.languages ?? [])].filter(Boolean); }, validateSupportedLanguages() { for (const blueprint of this.composedBlueprints) { if (blueprint.supportedLanguages) { this.supportedLanguages = [...this.supportedLanguages, ...blueprint.supportedLanguages]; } } if (this.languagesToApply.length > 0) { const unsupportedLanguage = this.languagesToApply.find(lang => !findLanguageForTag(lang, this.supportedLanguages)); if (unsupportedLanguage) { throw new Error(`Unsupported language "${unsupportedLanguage}" passed as argument to language generator.` + `\nSupported languages: ${this.supportedLanguages .map(language => `\n ${padEnd(language.languageTag, 5)} (${language.name})`) .join('')}`); } } }, validate() { if (this.languagesToApply.length > 0) { if (this.jhipsterConfig.skipClient) { this.log.log(chalk.bold(`\nInstalling languages: ${this.languagesToApply.join(', ')} for server`)); } else if (this.jhipsterConfig.skipServer) { this.log.log(chalk.bold(`\nInstalling languages: ${this.languagesToApply.join(', ')} for client`)); } else { this.log.log(chalk.bold(`\nInstalling languages: ${this.languagesToApply.join(', ')}`)); } } }, exportControl({ control }) { control.supportedLanguages = this.supportedLanguages; }, }); } get [BaseApplicationGenerator.INITIALIZING]() { return this.delegateTasksToBlueprint(() => this.initializing); } // Public API method used by the getter and also by Blueprints get prompting() { return this.asPromptingTaskGroup({ checkPrompts({ control }) { const { enableTranslation, languages } = this.jhipsterConfig; const showPrompts = this.options.askAnswered || this.languageCommand; this.askForNativeLanguage = showPrompts || (!control.existingProject && !this.jhipsterConfig.nativeLanguage); this.askForMoreLanguages = enableTranslation !== false && (showPrompts || (!control.existingProject && (languages?.length ?? 0) < 1)); }, askI18n, askForLanguages, }); } get [BaseApplicationGenerator.PROMPTING]() { return this.delegateTasksToBlueprint(() => this.prompting); } // Public API method used by the getter and also by Blueprints get configuring() { return this.asConfiguringTaskGroup({ migrateLanguages() { if (this.isJhipsterVersionLessThan('7.10.0')) { this.migrateLanguages({ in: 'id' }); } }, defaults() { const { nativeLanguage, enableTranslation } = this.jhipsterConfigWithDefaults; const isLanguageConfigured = Boolean(this.jhipsterConfig.nativeLanguage); // Prompts detects current language. Save default native language for next execution. this.config.defaults({ nativeLanguage: nativeLanguage }); if (!enableTranslation) { return; } this.config.defaults({ languages: [] }); if (!isLanguageConfigured && this.languagesToApply.length === 0) { // If languages is not configured, apply defaults. this.languagesToApply = this.jhipsterConfigWithDefaults.languages; } if (this.jhipsterConfig.languages.length === 0 || this.jhipsterConfig.languages[0] !== this.jhipsterConfig.nativeLanguage) { // Set native language as first language. this.jhipsterConfig.languages = [...new Set([nativeLanguage, ...this.jhipsterConfig.languages])]; } if (this.languagesToApply && this.languagesToApply.length > 0) { // Save new languages. this.jhipsterConfig.languages = [...new Set([...this.jhipsterConfig.languages, ...this.languagesToApply])]; } }, }); } get [BaseApplicationGenerator.CONFIGURING]() { return this.delegateTasksToBlueprint(() => this.configuring); } // Public API method used by the getter and also by Blueprints get preparing() { return this.asPreparingTaskGroup({ prepareForTemplates({ application, source }) { if (application.enableTranslation) { if (!this.languageCommand || this.regenerateLanguages) { this.languagesToApply = application.languages; } else { this.languagesToApply = [...new Set(this.languagesToApply || [])]; } } source.addEntityTranslationKey = ({ translationKey, translationValue, language }) => { this.mergeDestinationJson(`${application.clientSrcDir}i18n/${language}/global.json`, { global: { menu: { entities: { [translationKey]: translationValue, }, }, }, }); }; }, }); } get [BaseApplicationGenerator.PREPARING]() { return this.delegateTasksToBlueprint(() => this.preparing); } get default() { return this.asDefaultTaskGroup({ async loadNativeLanguage({ application, control }) { if (application.skipClient) return; control.translations = control.translations ?? {}; this.translationData = new TranslationData({ generator: this, translations: control.translations }); const { clientSrcDir, enableTranslation, nativeLanguage } = application; const fallbackLanguage = 'en'; this.queueLoadLanguages({ clientSrcDir, enableTranslation, nativeLanguage, fallbackLanguage }); const filter = createTranslationsFilter({ clientSrcDir, nativeLanguage, fallbackLanguage }); const listener = filePath => { if (filter(filePath)) { this.env.sharedFs.removeListener('change', listener); this.queueLoadLanguages({ clientSrcDir, enableTranslation, nativeLanguage, fallbackLanguage }); } }; this.env.sharedFs.on('change', listener); control.getWebappTranslation = (...args) => this.translationData.getClientTranslation(...args); }, }); } get [BaseApplicationGenerator.DEFAULT]() { return this.delegateTasksToBlueprint(() => this.default); } // Public API method used by the getter and also by Blueprints get writing() { return this.asWritingTaskGroup({ async writeClientTranslations({ application }) { if (application.skipClient) return; const languagesToApply = application.enableTranslation ? this.languagesToApply : [...new Set([application.nativeLanguage, 'en'])]; await Promise.all(languagesToApply.map(lang => this.writeFiles({ sections: clientI18nFiles, context: { ...application, lang, }, }))); }, async translateFile({ application }) { if (!application.enableTranslation || application.skipServer || (!application.backendTypeSpringBoot && !this.writeJavaLanguageFiles) || this.options.skipPriorities?.includes?.(PRIORITY_NAMES.POST_WRITING)) return; await Promise.all(this.languagesToApply.map(async (lang) => { const language = findLanguageForTag(lang); if (language.javaLocaleMessageSourceSuffix) { await this.writeFiles({ sections: { serverI18nFiles: [ { path: SERVER_MAIN_RES_DIR, renameTo: (data, filePath) => `${data.srcMainResources}${filePath}`, templates: [`i18n/messages_${language.javaLocaleMessageSourceSuffix}.properties`], }, ], serverI18nTestFiles: [ { path: SERVER_TEST_RES_DIR, renameTo: (data, filePath) => `${data.srcTestResources}${filePath}`, condition: data => !data.skipUserManagement, templates: [`i18n/messages_${language.javaLocaleMessageSourceSuffix}.properties`], }, ], }, context: { ...application, lang, }, }); } })); }, }); } get [BaseApplicationGenerator.WRITING]() { return this.delegateTasksToBlueprint(() => this.writing); } get writingEntities() { return this.asWritingEntitiesTaskGroup({ ...writeEntityFiles(), }); } get [BaseApplicationGenerator.WRITING_ENTITIES]() { return this.delegateTasksToBlueprint(() => this.writingEntities); } get postWriting() { return this.asPostWritingTaskGroup({ write({ application, control, source }) { if (this.options.skipPriorities?.includes?.(PRIORITY_NAMES.POST_WRITING)) return; const { enableTranslation, skipClient, languagesDefinition } = application; if (languagesDefinition && languagesDefinition.length > 0) { source.addLanguagesInFrontend?.({ languagesDefinition }); } if (enableTranslation && !skipClient) { if (application.clientFrameworkAngular) { updateLanguagesInAngularTask.call(this, { application, control }); } if (application.clientFrameworkReact) { updateLanguagesInReact.call(this, { application, control }); } if (application.clientFrameworkVue) { updateLanguagesInVue.call(this, { application, control }); } } if (application.enableTranslation && application.generateUserManagement && !application.skipServer && application.backendTypeJavaAny && application.backendTypeSpringBoot) { updateLanguagesInJava.call(this, { application, control, source }); } }, }); } get [BaseApplicationGenerator.POST_WRITING]() { return this.delegateTasksToBlueprint(() => this.postWriting); } get postWritingEntities() { return this.asPostWritingEntitiesTaskGroup({ addEntities({ application, entities, source }) { if (application.skipClient) return; const languagesToApply = application.enableTranslation ? this.languagesToApply : [...new Set([application.nativeLanguage, 'en'])]; for (const entity of entities.filter(entity => !entity.skipClient && !entity.builtInUser)) { for (const language of languagesToApply) { source.addEntityTranslationKey?.({ language, translationKey: entity.entityTranslationKeyMenu, translationValue: entity.entityClassHumanized ?? startCase(entity.entityClass), }); } } }, }); } get [BaseApplicationGenerator.POST_WRITING_ENTITIES]() { return this.delegateTasksToBlueprint(() => this.postWritingEntities); } migrateLanguages(languagesToMigrate) { const { languages, nativeLanguage } = this.jhipsterConfig; if (languagesToMigrate[nativeLanguage]) { this.jhipsterConfig.nativeLanguage = languagesToMigrate[nativeLanguage]; } if (languages?.some(lang => languagesToMigrate[lang])) { this.jhipsterConfig.languages = languages.map(lang => languagesToMigrate[lang] ?? lang); } } queueLoadLanguages({ enableTranslation, clientSrcDir, nativeLanguage, fallbackLanguage = 'en' }) { this.queueTask({ method: async () => { const filter = createTranslationsFileFilter({ clientSrcDir, nativeLanguage, fallbackLanguage }); await this.pipeline({ name: 'loading translations', filter: file => file.path.startsWith(this.destinationPath()) && filter(file), refresh: true, }, this.translationData.loadFromStreamTransform({ enableTranslation, clientSrcDir, nativeLanguage, fallbackLanguage, })); }, taskName: 'loadingTranslations', queueName: QUEUES.LOADING_TRANSLATIONS_QUEUE, once: true, }); } }