diff --git a/jest.config.js b/jest.config.js index a8176778..03d143d1 100644 --- a/jest.config.js +++ b/jest.config.js @@ -23,6 +23,10 @@ module.exports = { 'src/lib/*.js', 'src/*.js' ], + // Jest cannot execute import() here without --experimental-vm-modules; the module is covered at runtime. + coveragePathIgnorePatterns: [ + '/src/lib/create-yeoman-environment.js' + ], coverageThreshold: { global: { branches: 100, diff --git a/package.json b/package.json index 09519e98..26eef753 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "unzipper": "^0.10.11", "upath": "^2", "which": "^4.0.0", - "yeoman-environment": "^3.2.0" + "yeoman-environment": "^4.4.3" }, "devDependencies": { "@adobe/aio-lib-test-proxy": "^2", @@ -58,14 +58,14 @@ "eol": "^0.9.1", "eslint": "^9", "eslint-plugin-jsdoc": "^48.11.0", - "neostandard": "^0", "jest": "^29.5.0", + "neostandard": "^0", "nock": "^13.2.9", "oclif": "^4.17.13", "stdout-stderr": "^0.1.9" }, "engines": { - "node": ">=20" + "node": ">=20.5.0" }, "files": [ "bin/run", diff --git a/src/commands/app/add/ci.js b/src/commands/app/add/ci.js index e879fb6b..3b30587b 100644 --- a/src/commands/app/add/ci.js +++ b/src/commands/app/add/ci.js @@ -10,7 +10,7 @@ governing permissions and limitations under the License. */ const BaseCommand = require('../../../BaseCommand') -const yeoman = require('yeoman-environment') +const { createYeomanEnvironment } = require('../../../lib/create-yeoman-environment') const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:add:ci', { provider: 'debug' }) const generators = require('@adobe/generator-aio-app') @@ -20,10 +20,9 @@ class AddCICommand extends BaseCommand { aioLogger.debug(`adding component ${args.component} to the project, using flags: ${flags}`) - const env = yeoman.createEnv() + const env = await createYeomanEnvironment({ skipInstall: true }) // by default yeoman runs the install, we control installation from the app plugin - env.options = { skipInstall: true } - const gen = env.instantiate( + const gen = await env.instantiate( generators['add-ci'], { options: { } }) diff --git a/src/commands/app/init.js b/src/commands/app/init.js index ea0f16a5..f830aafd 100644 --- a/src/commands/app/init.js +++ b/src/commands/app/init.js @@ -10,7 +10,7 @@ governing permissions and limitations under the License. */ const TemplatesCommand = require('../../TemplatesCommand') -const yeoman = require('yeoman-environment') +const { createYeomanEnvironment } = require('../../lib/create-yeoman-environment') const path = require('path') const fs = require('fs-extra') const ora = require('ora') @@ -430,12 +430,11 @@ class InitCommand extends TemplatesCommand { } async runCodeGenerators (generatorNames, skipPrompt, projectName, linter) { - const env = yeoman.createEnv() - env.options = { skipInstall: true } + const env = await createYeomanEnvironment({ skipInstall: true }) // first run app generator that will generate the root skeleton + ci for (const generatorKey of generatorNames) { - const appGen = env.instantiate(generators[generatorKey], { + const appGen = await env.instantiate(generators[generatorKey], { options: { 'skip-prompt': skipPrompt, 'project-name': projectName, diff --git a/src/lib/create-yeoman-environment.js b/src/lib/create-yeoman-environment.js new file mode 100644 index 00000000..5ea6966e --- /dev/null +++ b/src/lib/create-yeoman-environment.js @@ -0,0 +1,24 @@ +/* +Copyright 2026 Adobe. All rights reserved. +This file is licensed to you 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 http://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 REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +/** + * yeoman-environment 4+ is ESM-only (no require() entry). Load it lazily so CommonJS + * commands can still run on Node 20+. + * + * @param {Record} [options] constructor options (e.g. skipInstall) + * @returns {Promise} a yeoman-environment instance (createEnv result) + */ +async function createYeomanEnvironment (options) { + const { createEnv } = await import('yeoman-environment') + return createEnv(options) +} + +module.exports = { createYeomanEnvironment } diff --git a/test/commands/app/add/ci.test.js b/test/commands/app/add/ci.test.js index aca145ca..ea76a08c 100644 --- a/test/commands/app/add/ci.test.js +++ b/test/commands/app/add/ci.test.js @@ -17,20 +17,19 @@ const generators = require('@adobe/generator-aio-app') jest.mock('fs-extra') -jest.mock('yeoman-environment') -const yeoman = require('yeoman-environment') +jest.mock('../../../../src/lib/create-yeoman-environment') +const { createYeomanEnvironment } = require('../../../../src/lib/create-yeoman-environment') const mockInstantiate = jest.fn() const mockRunGenerator = jest.fn() -yeoman.createEnv.mockReturnValue({ +createYeomanEnvironment.mockResolvedValue({ instantiate: mockInstantiate, runGenerator: mockRunGenerator }) - beforeEach(() => { mockInstantiate.mockReset() mockRunGenerator.mockReset() - yeoman.createEnv.mockClear() + createYeomanEnvironment.mockClear() fs.ensureDirSync.mockClear() }) @@ -50,7 +49,7 @@ describe('bad flags', () => { describe('template module cannot be registered', () => { test('unknown error', async () => { - mockInstantiate.mockImplementation(() => { throw new Error('some error') }) + mockInstantiate.mockRejectedValue(new Error('some error')) await expect(TheCommand.run([])).rejects.toThrow('some error') }) }) @@ -59,7 +58,7 @@ describe('no flags', () => { test('should pass', async () => { await TheCommand.run([]) - expect(yeoman.createEnv).toHaveBeenCalled() + expect(createYeomanEnvironment).toHaveBeenCalledWith({ skipInstall: true }) expect(mockInstantiate).toHaveBeenCalledWith(generators['add-ci'], { options: { } }) expect(mockRunGenerator).toHaveBeenCalled() }) diff --git a/test/commands/app/init.test.js b/test/commands/app/init.test.js index 756b10e5..c82932c3 100644 --- a/test/commands/app/init.test.js +++ b/test/commands/app/init.test.js @@ -16,7 +16,7 @@ const BaseCommand = require('../../../src/BaseCommand') const importHelperLib = require('../../../src/lib/import-helper') const inquirer = require('inquirer') const savedDataDir = process.env.XDG_DATA_HOME -const yeoman = require('yeoman-environment') +const { createYeomanEnvironment } = require('../../../src/lib/create-yeoman-environment') const { Octokit } = require('@octokit/rest') jest.mock('@adobe/aio-lib-core-config') @@ -83,8 +83,8 @@ function resetMockConsoleCLI () { mockConsoleCLIInstance.prompt.promptConfirm.mockReset() } -jest.mock('yeoman-environment') -yeoman.createEnv.mockReturnValue({ +jest.mock('../../../src/lib/create-yeoman-environment') +createYeomanEnvironment.mockResolvedValue({ instantiate: jest.fn(), runGenerator: jest.fn() })