diff --git a/.github/workflows/test-acceptance.yaml b/.github/workflows/test-acceptance.yaml index ddc65f39b1..7b879b4acf 100644 --- a/.github/workflows/test-acceptance.yaml +++ b/.github/workflows/test-acceptance.yaml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false # continue other tests if one test in matrix fails matrix: - node-version: [18.x] + node-version: [20.x] os: [macos-latest, windows-latest, ubuntu-latest] type: [smoke, plugins, styles, dev, prod, errors] diff --git a/.github/workflows/test-heroku.yaml b/.github/workflows/test-heroku.yaml index cbfbd27171..1b15ea5544 100644 --- a/.github/workflows/test-heroku.yaml +++ b/.github/workflows/test-heroku.yaml @@ -22,7 +22,7 @@ jobs: uses: actions/setup-node@v3 with: cache: 'npm' - node-version: '18' + node-version: '20' - name: Cache Cypress binary uses: actions/cache@v3 with: diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index fe8433fdaa..eb611e3151 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false # continue other tests if one test in matrix fails matrix: - node-version: [16.x, 18.x] + node-version: [16.x, 18.x, 20.x] os: [macos-latest, windows-latest, ubuntu-latest] name: Test kit on Node v${{ matrix.node-version }} (${{ matrix.os }}) diff --git a/.nvmrc b/.nvmrc index b009dfb9d9..9de2256827 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -lts/* +lts/iron diff --git a/CHANGELOG.md b/CHANGELOG.md index 68647c8251..36cebc0ed1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### New features + +- [#2363: Support for the new Node LTS (version 20)](https://github.com/alphagov/govuk-prototype-kit/pull/2363) + ## 13.13.6 ### Fixes diff --git a/bin/cli b/bin/cli index 27f9f73c3d..bafa217d0f 100755 --- a/bin/cli +++ b/bin/cli @@ -2,16 +2,16 @@ if (process.argv.indexOf('--suppress-node-version-warning') === -1 || (process.argv[2] === 'migrate' && process.argv[3] === '--')) { const majorNodeVersion = parseInt(process.version.split('.')[0].substring(1), 10) - const versionIsWithinRecommendation = majorNodeVersion === 18 || majorNodeVersion === 16 + const versionIsWithinRecommendation = [16, 18, 20].includes(majorNodeVersion) if (!versionIsWithinRecommendation) { const nodeVersionIsTooOldToUse = majorNodeVersion < 14 - const updateOrDownload = majorNodeVersion < 18 ? 'update to' : 'download' + const updateOrDownload = majorNodeVersion < 20 ? 'update to' : 'download' const printLn = nodeVersionIsTooOldToUse ? console.error.bind(console) : console.warn.bind(console) const additionalText = nodeVersionIsTooOldToUse ? '' : ' Some features may not work with your version.' printLn('\nYou\'re using Node', process.version) - printLn('The GOV.UK Prototype Kit only supports Node v16 and v18.' + additionalText + '\n') - printLn('You can', updateOrDownload, 'Node v18 at https://nodejs.org/en/download\n') + printLn('The GOV.UK Prototype Kit only supports Node v16, v18 and v20.' + additionalText + '\n') + printLn('You can', updateOrDownload, 'Node v20 at https://nodejs.org/en/download\n') if (nodeVersionIsTooOldToUse) { process.exit(0) diff --git a/lib/plugins/plugin-validator.spec.js b/lib/plugins/plugin-validator.spec.js index 50dbf5a1ea..e98cca58f4 100644 --- a/lib/plugins/plugin-validator.spec.js +++ b/lib/plugins/plugin-validator.spec.js @@ -13,12 +13,23 @@ describe('plugin-validator', () => { const validSuccessMessages = phaseOneSuccessMessages.concat([ansiColors.green('The plugin config is valid.')]) beforeEach(() => { + // Polyfill process.exitCode property for Node.js < v20 + const originalExitCode = process.exitCode + if (!process.exitCode) { + Object.defineProperty(process, 'exitCode', { + get: jest.fn(), + set: jest.fn() + }) + } const mockFileSystemRoot = path.join(process.cwd(), 'example-plugin') + const exitCodeSetterSpy = jest.spyOn(process, 'exitCode', 'set').mockImplementation(() => {}) testScope = { mockFileSystemRoot, fileSystem: mockFileSystem(mockFileSystemRoot), stdLogs: [], - errLogs: [] + errLogs: [], + exitCodeSetterSpy, + originalExitCode } jest.spyOn(console, 'log').mockImplementation((message, ...args) => { if (args.length > 0) { @@ -40,6 +51,8 @@ describe('plugin-validator', () => { afterEach(() => { jest.clearAllMocks() + // Reset polyfill process.exitCode property for Node.js < v20 + process.exitCode = testScope.originalExitCode }) describe('unexpected keys', () => { @@ -50,6 +63,7 @@ describe('plugin-validator', () => { await validatePlugin(testScope.mockFileSystemRoot) expect(testScope.errLogs).toEqual([ansiColors.red('Error: The following invalid keys exist in your config: myExample')]) expect(testScope.stdLogs).toEqual(phaseOneSuccessMessages) + expect(testScope.exitCodeSetterSpy).toHaveBeenCalledWith(100) }) it('should allow a single unknown key when specified', async () => { testScope.fileSystem.writeFile(['govuk-prototype-kit.config.json'], JSON.stringify({ diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 0927544a30..deea975653 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -56,7 +56,7 @@ "wait-on": "^7.0.1" }, "engines": { - "node": "^16.x || >= 18.x" + "node": "^18.x || >= 20.x" } }, "node_modules/@aashutoshrathi/word-wrap": { diff --git a/package.json b/package.json index 6ab539bedc..fa9583c557 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "Rapidly create HTML prototypes of GOV.UK services", "version": "13.13.6", "engines": { - "node": "^16.x || >= 18.x" + "node": "^16.x || ^18.x || >= 20.x" }, "main": "index.js", "bin": {