diff --git a/generators/base-core/generator.ts b/generators/base-core/generator.ts index fe753fc6561f..3114761d0960 100644 --- a/generators/base-core/generator.ts +++ b/generators/base-core/generator.ts @@ -172,7 +172,6 @@ export default class CoreGenerator extends YeomanGenerator { + private createSharedData({ help }: { help?: boolean }): SharedData { const applicationId = this.options.applicationId ?? this.calculateApplicationId(this.destinationPath()); if (this.options.sharedData.applications === undefined) { this.options.sharedData.applications = {}; @@ -1272,6 +1264,10 @@ templates: ${JSON.stringify(existingTemplates, null, 2)}`; } const { ignoreNeedlesError } = this.options; - return new SharedData(sharedApplications[applicationId], { jhipsterOldVersion, ignoreNeedlesError }); + return new SharedData( + sharedApplications[applicationId], + { destinationPath: this.destinationPath(), memFs: this.env.sharedFs, log: this.log, logCwd: this.env.logCwd }, + { ignoreNeedlesError }, + ); } } diff --git a/generators/base/shared-data.ts b/generators/base/shared-data.ts index 1e11725feaf0..aa495a655f9c 100644 --- a/generators/base/shared-data.ts +++ b/generators/base/shared-data.ts @@ -16,17 +16,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { existsSync, readFileSync, statSync } from 'fs'; +import { rm } from 'fs/promises'; +import { isAbsolute, join, relative } from 'path'; +import { lt as semverLessThan } from 'semver'; import { defaults } from 'lodash-es'; +import { MemFsEditor, create } from 'mem-fs-editor'; import { type BaseApplication } from '../base-application/types.js'; import { type Control } from './types.js'; +import { GENERATOR_JHIPSTER } from '../generator-constants.js'; export default class SharedData { _storage: any; + _editor: MemFsEditor; + _log: any; + _logCwd: string; - constructor(storage, initialControl: Partial = {}) { + constructor(storage, { memFs, destinationPath, log, logCwd }, initialControl: Partial = {}) { if (!storage) { throw new Error('Storage is required for SharedData'); } + + this._editor = create(memFs); + this._log = log; + this._logCwd = logCwd; + + let jhipsterOldVersion; + if (existsSync(join(destinationPath, '.yo-rc.json'))) { + jhipsterOldVersion = JSON.parse(readFileSync(join(destinationPath, '.yo-rc.json'), 'utf-8').toString())[GENERATOR_JHIPSTER] + ?.jhipsterVersion; + } + // Backward compatibility sharedData this._storage = storage; @@ -44,6 +64,55 @@ export default class SharedData string | undefined> = []; + const removeFiles = async (assertions: { removedInVersion?: string } | string, ...files: string[]) => { + if (typeof assertions === 'string') { + files = [assertions, ...files]; + assertions = {}; + } + + for (const customize of customizeRemoveFiles) { + files = files.map(customize).filter(file => file) as string[]; + } + + const { removedInVersion } = assertions; + if (removedInVersion && jhipsterOldVersion && !semverLessThan(jhipsterOldVersion, removedInVersion)) { + return; + } + + const absolutePaths = files.map(file => (isAbsolute(file) ? file : join(destinationPath, file))); + // Delete from memory fs to keep updated. + this._editor.delete(absolutePaths); + await Promise.all( + absolutePaths.map(async file => { + const relativePath = relative(logCwd, file); + try { + if (statSync(file).isFile()) { + this._log.info(`Removing legacy file ${relativePath}`); + await rm(file, { force: true }); + } + } catch { + this._log.info(`Could not remove legacy file ${relativePath}`); + } + }), + ); + }; + + defaults(this._storage.control, { + jhipsterOldVersion, + removeFiles, + customizeRemoveFiles: [], + cleanupFiles: async (cleanup: Record) => { + await Promise.all( + Object.entries(cleanup).map(async ([version, files]) => { + await removeFiles({ removedInVersion: version }, ...files); + }), + ); + }, + }); + + customizeRemoveFiles = this._storage.control.customizeRemoveFiles; } getSource() { diff --git a/generators/base/types.d.ts b/generators/base/types.d.ts index 10b345c124d7..a8bdf235aaf2 100644 --- a/generators/base/types.d.ts +++ b/generators/base/types.d.ts @@ -13,4 +13,7 @@ export type Control = { reproducibleLiquibaseTimestamp?: Date; filterEntitiesForClient?: (entity: Entity[]) => Entity[]; filterEntitiesAndPropertiesForClient?: (entity: Entity[]) => Entity[]; + customizeRemoveFiles: Array<(file: string) => string | undefined>; + removeFiles: (options: { removedInVersion: string } | string, ...files: string[]) => Promise; + cleanupFiles: (cleanup: Record) => Promise; }; diff --git a/generators/generate-blueprint/generator.js b/generators/generate-blueprint/generator.js index 0e9a6a9b6ecb..58fbd5f460b1 100644 --- a/generators/generate-blueprint/generator.js +++ b/generators/generate-blueprint/generator.js @@ -173,7 +173,7 @@ export default class extends BaseGenerator { } get preparing() { - return { + return this.asPreparingTaskGroup({ prepareCommands() { this.application.commands = []; this.application.nodeVersion = NODE_VERSION; @@ -195,7 +195,7 @@ export default class extends BaseGenerator { this.application.cliName = cliName ?? `jhipster-${baseName}`; } }, - }; + }); } get [BaseGenerator.PREPARING]() { @@ -203,7 +203,10 @@ export default class extends BaseGenerator { } get writing() { - return { + return this.asWritingTaskGroup({ + async cleanup({ control }) { + await control.cleanupFiles({ '8.5.1': ['.eslintrc.json'] }); + }, async writing() { this.application.sampleWritten = this.jhipsterConfig.sampleWritten; await this.writeFiles({ @@ -243,7 +246,7 @@ export default class extends BaseGenerator { subGeneratorStorage.set(WRITTEN, true); } }, - }; + }); } get [BaseGenerator.WRITING]() { diff --git a/generators/languages/generator.spec.js b/generators/languages/generator.spec.js index 1f22a527ecb4..76ccfd8ec7ea 100644 --- a/generators/languages/generator.spec.js +++ b/generators/languages/generator.spec.js @@ -49,7 +49,8 @@ describe(`generator - ${generator}`, () => { nativeLanguage: 'in', languages: ['in'], baseName: 'jhipster', - }), + }) + .commitFiles(), ); it('should migrate in language to id', () => { runResult.assertJsonFileContent('.yo-rc.json', {