From 8609b6f28b66ffbf3158e9df825d18d62a22a966 Mon Sep 17 00:00:00 2001 From: David Ortner Date: Tue, 26 Mar 2024 16:16:19 +0100 Subject: [PATCH 1/4] fix: [#1367] Fixes problem with Document.defaultView not referring to the global object when using GlobalRegistrator --- packages/global-registrator/README.md | 2 +- .../src/GlobalRegistrator.ts | 37 ++++++----- .../test/react/React.test.tsx | 29 ++++++++- packages/happy-dom/src/index.ts | 6 +- .../happy-dom/src/window/BrowserWindow.ts | 64 +++++++++++-------- 5 files changed, 91 insertions(+), 47 deletions(-) diff --git a/packages/global-registrator/README.md b/packages/global-registrator/README.md index dd40170b..f1ecb19c 100644 --- a/packages/global-registrator/README.md +++ b/packages/global-registrator/README.md @@ -32,7 +32,7 @@ import { GlobalRegistrator } from '@happy-dom/global-registrator'; GlobalRegistrator.register(); -GlobalRegistrator.unregister(); +await GlobalRegistrator.unregister(); // Outputs: "undefined" console.log(global.document); diff --git a/packages/global-registrator/src/GlobalRegistrator.ts b/packages/global-registrator/src/GlobalRegistrator.ts index 525f1e1d..d7426c75 100644 --- a/packages/global-registrator/src/GlobalRegistrator.ts +++ b/packages/global-registrator/src/GlobalRegistrator.ts @@ -1,14 +1,13 @@ -import { GlobalWindow } from 'happy-dom'; +import { GlobalWindow, PropertySymbol } from 'happy-dom'; import type { IOptionalBrowserSettings } from 'happy-dom'; const IGNORE_LIST = ['constructor', 'undefined', 'NaN', 'global', 'globalThis']; -const SELF_REFERRING = ['self', 'top', 'parent', 'window']; /** * */ export default class GlobalRegistrator { - private static registered: { [key: string]: PropertyDescriptor } | null = null; + private static registered: { [key: string | symbol]: PropertyDescriptor } | null = null; /** * Registers Happy DOM globally. @@ -47,37 +46,39 @@ export default class GlobalRegistrator { ) { this.registered[key] = globalPropertyDescriptor || null; - if ( - typeof windowPropertyDescriptor.value === 'function' && - !windowPropertyDescriptor.value.toString().startsWith('class ') - ) { - Object.defineProperty(global, key, { - ...windowPropertyDescriptor, - value: windowPropertyDescriptor.value.bind(global) - }); - } else { - Object.defineProperty(global, key, windowPropertyDescriptor); + if (windowPropertyDescriptor.value === window) { + window[key] = global; + windowPropertyDescriptor.value = global; } + + Object.defineProperty(global, key, windowPropertyDescriptor); } } } - for (const key of SELF_REFERRING) { + const propertySymbols = Object.getOwnPropertySymbols(window); + for (const key of propertySymbols) { + const propertyDescriptor = Object.getOwnPropertyDescriptor(window, key); this.registered[key] = null; - global[key] = global; + Object.defineProperty(global, key, propertyDescriptor); } + + global.document[PropertySymbol.ownerWindow] = global; + global.document[PropertySymbol.defaultView] = global; } /** * Registers Happy DOM globally. */ - public static unregister(): void { + public static async unregister(): Promise { if (this.registered === null) { throw new Error( 'Failed to unregister. Happy DOM has not previously been globally registered.' ); } + const happyDOM = global.happyDOM; + for (const key of Object.keys(this.registered)) { if (this.registered[key] !== null) { Object.defineProperty(global, key, this.registered[key]); @@ -87,5 +88,9 @@ export default class GlobalRegistrator { } this.registered = null; + + if (happyDOM) { + await happyDOM.close(); + } } } diff --git a/packages/global-registrator/test/react/React.test.tsx b/packages/global-registrator/test/react/React.test.tsx index f612bb8d..c47bd0a0 100644 --- a/packages/global-registrator/test/react/React.test.tsx +++ b/packages/global-registrator/test/react/React.test.tsx @@ -41,6 +41,7 @@ async function main(): Promise { function testGetters(): void { const included: string[] = []; const propertyNames = Object.getOwnPropertyNames(global); + for (const name of GETTERS) { if (propertyNames.includes(name)) { included.push(name); @@ -121,6 +122,31 @@ async function main(): Promise { testLocationHref(); + /** + * Test CSS. + */ + function testCSS(): void { + const style = document.createElement('style'); + document.head.appendChild(style); + style.innerHTML = ` + body { + background-color: red; + } + + @media (min-width: 1000px) { + body { + background-color: green; + } + } + `; + + if (globalThis.getComputedStyle(document.body).backgroundColor !== 'green') { + throw Error('The CSS was not applied correctly.'); + } + } + + testCSS(); + /** * Unregisters Happy DOM globally. */ @@ -132,6 +158,7 @@ async function main(): Promise { function testGettersAfterUnregister(): void { const included: string[] = []; const propertyNames = Object.getOwnPropertyNames(global); + for (const name of GETTERS) { if (propertyNames.includes(name)) { included.push(name); @@ -140,7 +167,7 @@ async function main(): Promise { if (included.length !== 0) { throw Error( - 'Object.getOwnPropertyNames() did not remove all properties defined as getter. Expected: []. Got: ' + + 'GlobalObserver.unregister() did not remove all properties defined as getter. Expected: []. Got: ' + included.join(', ') + '.' ); diff --git a/packages/happy-dom/src/index.ts b/packages/happy-dom/src/index.ts index 491a9a76..74d7c43e 100644 --- a/packages/happy-dom/src/index.ts +++ b/packages/happy-dom/src/index.ts @@ -1,4 +1,5 @@ import { URLSearchParams } from 'url'; +import * as PropertySymbol from './PropertySymbol.js'; import Browser from './browser/Browser.js'; import BrowserContext from './browser/BrowserContext.js'; import BrowserFrame from './browser/BrowserFrame.js'; @@ -60,6 +61,7 @@ import File from './file/File.js'; import FileReader from './file/FileReader.js'; import FormData from './form-data/FormData.js'; import History from './history/History.js'; +import Location from './location/Location.js'; import MutationObserver from './mutation-observer/MutationObserver.js'; import MutationRecord from './mutation-observer/MutationRecord.js'; import Attr from './nodes/attr/Attr.js'; @@ -117,9 +119,9 @@ import Storage from './storage/Storage.js'; import NodeFilter from './tree-walker/NodeFilter.js'; import NodeIterator from './tree-walker/NodeIterator.js'; import TreeWalker from './tree-walker/TreeWalker.js'; -import Location from './location/Location.js'; import URL from './url/URL.js'; import BrowserWindow from './window/BrowserWindow.js'; +import DetachedWindowAPI from './window/DetachedWindowAPI.js'; import GlobalWindow from './window/GlobalWindow.js'; import Window from './window/Window.js'; import XMLParser from './xml-parser/XMLParser.js'; @@ -216,6 +218,7 @@ export { DetachedBrowserContext, DetachedBrowserFrame, DetachedBrowserPage, + DetachedWindowAPI, Document, DocumentFragment, DocumentType, @@ -322,6 +325,7 @@ export { Permissions, ProcessingInstruction, ProgressEvent, + PropertySymbol, Range, Request, ResizeObserver, diff --git a/packages/happy-dom/src/window/BrowserWindow.ts b/packages/happy-dom/src/window/BrowserWindow.ts index d9fd217e..14f2e005 100644 --- a/packages/happy-dom/src/window/BrowserWindow.ts +++ b/packages/happy-dom/src/window/BrowserWindow.ts @@ -531,34 +531,6 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal WindowBrowserSettingsReader.setSettings(this, this.#browserFrame.page.context.browser.settings); - // Binds getts and setters, so that they will appear as an "own" property when using Object.getOwnPropertyNames(). - // This is needed for Vitest to work as it relies on Object.getOwnPropertyNames() to get the list of properties. - // @see https://github.com/capricorn86/happy-dom/issues/1339 - // Binds all methods to "this", so that it will use the correct context when called globally. - const propertyDescriptors = Object.assign( - Object.getOwnPropertyDescriptors(EventTarget.prototype), - Object.getOwnPropertyDescriptors(BrowserWindow.prototype) - ); - for (const key of Object.keys(propertyDescriptors)) { - const descriptor = propertyDescriptors[key]; - if (descriptor.get || descriptor.set) { - Object.defineProperty(this, key, { - configurable: true, - enumerable: true, - get: descriptor.get?.bind(this), - set: descriptor.set?.bind(this) - }); - } else if ( - key !== 'constructor' && - key[0] !== '_' && - key[0] === key[0].toLowerCase() && - typeof this[key] === 'function' && - !this[key].toString().startsWith('class ') - ) { - this[key] = this[key].bind(this); - } - } - const window = this; const asyncTaskManager = this.#browserFrame[PropertySymbol.asyncTaskManager]; @@ -686,6 +658,8 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal this.document.dispatchEvent(new Event('readystatechange')); this.document.dispatchEvent(new Event('load', { bubbles: true })); }); + + this.#bindToThisScope(); } /** @@ -1349,4 +1323,38 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal WindowBrowserSettingsReader.removeSettings(this); } + + /** + * Binds methods, getters and setters to a scope. + * + * Getters and setters need to be bound to show up in Object.getOwnPropertyNames(), which is something Vitest relies on. + * + * @see https://github.com/capricorn86/happy-dom/issues/1339 + */ + #bindToThisScope(): void { + const propertyDescriptors = Object.assign( + Object.getOwnPropertyDescriptors(EventTarget.prototype), + Object.getOwnPropertyDescriptors(BrowserWindow.prototype) + ); + + for (const key of Object.keys(propertyDescriptors)) { + const descriptor = propertyDescriptors[key]; + if (descriptor.get || descriptor.set) { + Object.defineProperty(this, key, { + configurable: true, + enumerable: true, + get: descriptor.get?.bind(this), + set: descriptor.set?.bind(this) + }); + } else if ( + key !== 'constructor' && + key[0] !== '_' && + key[0] === key[0].toLowerCase() && + typeof this[key] === 'function' && + !this[key].toString().startsWith('class ') + ) { + this[key] = this[key].bind(this); + } + } + } } From 4154b433134a187363a50d8ee0136058f6b58935 Mon Sep 17 00:00:00 2001 From: David Ortner Date: Fri, 5 Apr 2024 00:05:35 +0200 Subject: [PATCH 2/4] chore: [#1367] Adds integration tests for Bun --- .github/workflows/pull_request.yml | 5 +- .github/workflows/release.yml | 5 +- docs/contributing.md | 38 +++++--- packages/global-registrator/package.json | 6 +- .../src/GlobalRegistrator.ts | 22 +++-- .../global-registrator/test/bun/Bun.test.js | 89 +++++++++++++++++++ .../test/react/React.test.tsx | 17 ++-- .../test/{ => react}/tsconfig.json | 8 +- 8 files changed, 156 insertions(+), 34 deletions(-) create mode 100644 packages/global-registrator/test/bun/Bun.test.js rename packages/global-registrator/test/{ => react}/tsconfig.json (68%) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 52a3af34..a273b450 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -48,9 +48,12 @@ jobs: path: .turbo key: turbo-master - - name: Install dependencies + - name: Install NPM dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' run: npm ci --ignore-scripts + + - name: Install Bun + run: curl -fsSL https://bun.sh/install | bash - name: Compile package run: npm run compile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 47395b5d..9a15b1e4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -88,10 +88,13 @@ jobs: restore-keys: | turbo-master - - name: Install dependencies + - name: Install NPM dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' run: npm ci --ignore-scripts + - name: Install Bun + run: curl -fsSL https://bun.sh/install | bash + - name: Set version run: node ./node_modules/.bin/happy-set-workspace-version --version=${{needs.check-next-version.outputs.next_version }} diff --git a/docs/contributing.md b/docs/contributing.md index 3c74842b..b4ee5a2d 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -22,23 +22,23 @@ npm run compile npm run watch ``` -### Debugging +### Test -1. Go to the package you wish to test in the terminal (e.g. "cd ./packages/happy-dom") -2. Write "debugger;" at the place you want to place a breakpoint in the code. -3. Run the following command in the terminal: +**Install Bun** + +To be able to run all tests in the "./packages/global-registrator" package, you need to install [Bun](https://bun.sh/). + +Linux & MacOS ```bash -npm run test:debug +curl -fsSL https://bun.sh/install | bash ``` -4. Open Chrome. -5. Open developer tools. -6. A green ball should appear to the left of the menu bar in developer tools. -7. Click on the green ball. -8. Click continue to jump to your breakpoint. +Windows -### Automated Tests +```bash +powershell -c "irm bun.sh/install.ps1 | iex" +``` **Run tests** @@ -52,6 +52,22 @@ npm test npm run test:watch ``` +### Debug + +1. Go to the package you wish to test in the terminal (e.g. "cd ./packages/happy-dom") +2. Write "debugger;" at the place you want to place a breakpoint in the code. +3. Run the following command in the terminal: + +```bash +npm run test:debug +``` + +4. Open Chrome. +5. Open developer tools. +6. A green ball should appear to the left of the menu bar in developer tools. +7. Click on the green ball. +8. Click continue to jump to your breakpoint. + # Commit Convention We use the [Conventional Commits](https://www.conventionalcommits.org/en/) standard for our commit messages. The description should start with an uppercase character. diff --git a/packages/global-registrator/package.json b/packages/global-registrator/package.json index 756dac10..5b4cbe48 100644 --- a/packages/global-registrator/package.json +++ b/packages/global-registrator/package.json @@ -70,8 +70,10 @@ "compile:cjs": "rm -rf cjs && tsc --moduleResolution Node --module CommonJS --outDir cjs && npm run compile:change-cjs-file-extension", "compile:change-cjs-file-extension": "node ../happy-dom/bin/change-file-extension.cjs --dir=./cjs --fromExt=.js --toExt=.cjs", "watch": "npm run compile && tsc -w --preserveWatchOutput", - "test": "rm -rf tmp && tsc --project ./test && node ../happy-dom/bin/change-file-extension.cjs --dir=./tmp --fromExt=.js --toExt=.cjs && node ./tmp/react/React.test.cjs", - "test:debug": "tsc --project ./test && node ../happy-dom/bin/change-file-extension.cjs --dir=./tmp --fromExt=.js --toExt=.cjs && node --inspect-brk ./tmp/react/React.test.cjs" + "test": "npm run test:react && npm run test:bun", + "test:debug": "tsc --project ./test/react && node ../happy-dom/bin/change-file-extension.cjs --dir=./tmp --fromExt=.js --toExt=.cjs && node --inspect-brk ./tmp/react/React.test.cjs", + "test:react": "rm -rf tmp && tsc --project ./test/react && node ../happy-dom/bin/change-file-extension.cjs --dir=./tmp --fromExt=.js --toExt=.cjs && node ./tmp/react/React.test.cjs", + "test:bun": "bun test ./test/bun/Bun.test.js" }, "dependencies": { "happy-dom": "^0.0.0" diff --git a/packages/global-registrator/src/GlobalRegistrator.ts b/packages/global-registrator/src/GlobalRegistrator.ts index d7426c75..a36c39e0 100644 --- a/packages/global-registrator/src/GlobalRegistrator.ts +++ b/packages/global-registrator/src/GlobalRegistrator.ts @@ -32,6 +32,7 @@ export default class GlobalRegistrator { this.registered = {}; + // Define properties on the global object const propertyDescriptors = Object.getOwnPropertyDescriptors(window); for (const key of Object.keys(propertyDescriptors)) { @@ -40,35 +41,44 @@ export default class GlobalRegistrator { const globalPropertyDescriptor = Object.getOwnPropertyDescriptor(global, key); if ( - !globalPropertyDescriptor || - (windowPropertyDescriptor.value !== undefined && - windowPropertyDescriptor.value !== globalPropertyDescriptor.value) + globalPropertyDescriptor?.value === undefined || + globalPropertyDescriptor?.value !== windowPropertyDescriptor.value ) { this.registered[key] = globalPropertyDescriptor || null; + // If the property is the window object, replace it with the global object if (windowPropertyDescriptor.value === window) { window[key] = global; windowPropertyDescriptor.value = global; } - Object.defineProperty(global, key, windowPropertyDescriptor); + Object.defineProperty(global, key, { + ...windowPropertyDescriptor, + configurable: true + }); } } } + // Define symbol properties on the global object const propertySymbols = Object.getOwnPropertySymbols(window); + for (const key of propertySymbols) { const propertyDescriptor = Object.getOwnPropertyDescriptor(window, key); this.registered[key] = null; - Object.defineProperty(global, key, propertyDescriptor); + Object.defineProperty(global, key, { + ...propertyDescriptor, + configurable: true + }); } + // Set owner window on document to global global.document[PropertySymbol.ownerWindow] = global; global.document[PropertySymbol.defaultView] = global; } /** - * Registers Happy DOM globally. + * Closes the window and unregisters Happy DOM from being global. */ public static async unregister(): Promise { if (this.registered === null) { diff --git a/packages/global-registrator/test/bun/Bun.test.js b/packages/global-registrator/test/bun/Bun.test.js new file mode 100644 index 00000000..fc3b2e88 --- /dev/null +++ b/packages/global-registrator/test/bun/Bun.test.js @@ -0,0 +1,89 @@ +import { GlobalRegistrator } from '../../lib/index.js'; +import { test, expect } from 'bun:test'; + +GlobalRegistrator.register(); + +/* eslint-disable no-undef */ + +const GETTERS = [ + 'location', + 'history', + 'navigator', + 'screen', + 'sessionStorage', + 'localStorage', + 'opener', + 'scrollX', + 'pageXOffset', + 'scrollY', + 'pageYOffset', + 'CSS', + 'innerWidth', + 'innerHeight', + 'outerWidth', + 'outerHeight', + 'devicePixelRatio' +]; + +test('DOM', () => { + document.body.innerHTML = ``; + const button = document.querySelector('button'); + expect(button?.innerText).toEqual('My button'); +}); + +test('CSS', () => { + const style = document.createElement('style'); + + document.head.appendChild(style); + style.innerHTML = ` + body { + background-color: red; + } + + @media (min-width: 1000px) { + body { + background-color: green; + } + } + `; + + expect(globalThis.getComputedStyle(document.body).backgroundColor).toBe('green'); +}); + +test('Window getters', () => { + const included = []; + const propertyNames = Object.getOwnPropertyNames(global); + + for (const name of GETTERS) { + if (propertyNames.includes(name)) { + included.push(name); + } + } + + expect(included).toEqual(GETTERS); +}); + +test('Window location', () => { + globalThis.location.href = 'https://example.com/'; + expect(globalThis.location.href).toBe('https://example.com/'); +}); + +test('Window options', () => { + GlobalRegistrator.unregister(); + + GlobalRegistrator.register({ + url: 'https://example.com/', + width: 1920, + height: 1080, + settings: { + navigator: { + userAgent: 'Custom User Agent' + } + } + }); + + expect(globalThis.location.href).toBe('https://example.com/'); + expect(globalThis.innerWidth).toBe(1920); + expect(globalThis.innerHeight).toBe(1080); + expect(globalThis.navigator.userAgent).toBe('Custom User Agent'); +}); diff --git a/packages/global-registrator/test/react/React.test.tsx b/packages/global-registrator/test/react/React.test.tsx index c47bd0a0..f23a987c 100644 --- a/packages/global-registrator/test/react/React.test.tsx +++ b/packages/global-registrator/test/react/React.test.tsx @@ -50,11 +50,7 @@ async function main(): Promise { if (included.length !== GETTERS.length) { throw Error( - 'Object.getOwnPropertyNames() did not return all properties defined as getter. Expected: ' + - GETTERS.join(', ') + - '. Got: ' + - included.join(', ') + - '.' + `Object.getOwnPropertyNames() did not return all properties defined as getter. Expected: "${GETTERS.join(', ')}", Got: "${included.join(', ')}".` ); } } @@ -165,11 +161,14 @@ async function main(): Promise { } } - if (included.length !== 0) { + // In Node.js v21 and later, the navigator property is available. + if (!included.includes('navigator')) { + included.push('navigator'); + } + + if (included.length !== 1 || included[0] !== 'navigator') { throw Error( - 'GlobalObserver.unregister() did not remove all properties defined as getter. Expected: []. Got: ' + - included.join(', ') + - '.' + `GlobalObserver.unregister() did not remove all properties defined as getter. Expected: "navigator", Got: "${included.join(', ')}".` ); } } diff --git a/packages/global-registrator/test/tsconfig.json b/packages/global-registrator/test/react/tsconfig.json similarity index 68% rename from packages/global-registrator/test/tsconfig.json rename to packages/global-registrator/test/react/tsconfig.json index ec39db5d..f4dce54f 100644 --- a/packages/global-registrator/test/tsconfig.json +++ b/packages/global-registrator/test/react/tsconfig.json @@ -1,8 +1,8 @@ { - "extends": "../tsconfig.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "outDir": "../tmp", - "rootDir": "../test", + "outDir": "../../tmp", + "rootDir": "../../test", "jsx": "react", "module": "CommonJS", "moduleResolution": "Node", @@ -16,6 +16,6 @@ "include": [ "@types/node", ".", - "../lib" + "../../lib" ] } \ No newline at end of file From 3c0bf9faa5123c246de3cb0a3b1ef7383221890b Mon Sep 17 00:00:00 2001 From: David Ortner Date: Fri, 5 Apr 2024 00:10:03 +0200 Subject: [PATCH 3/4] chore: [#1367] Adds integration tests for Bun --- docs/contributing.md | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index b4ee5a2d..7a0f0f4f 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -6,40 +6,42 @@ We are very happy that you would like to contribute. In this guide you will find ### Install -```bash -npm install -``` +**Install Bun** -### Compile +To be able to run all tests in the "./packages/global-registrator" package, you need to install [Bun](https://bun.sh/). + +Linux & MacOS ```bash -npm run compile +curl -fsSL https://bun.sh/install | bash ``` -### Watch +Windows ```bash -npm run watch +powershell -c "irm bun.sh/install.ps1 | iex" ``` -### Test +**Install dependencies** -**Install Bun** - -To be able to run all tests in the "./packages/global-registrator" package, you need to install [Bun](https://bun.sh/). +```bash +npm install +``` -Linux & MacOS +### Compile ```bash -curl -fsSL https://bun.sh/install | bash +npm run compile ``` -Windows +### Watch ```bash -powershell -c "irm bun.sh/install.ps1 | iex" +npm run watch ``` +### Test + **Run tests** ```bash From fd45d3ca78af22237bdfa3c533b46a102df51cd0 Mon Sep 17 00:00:00 2001 From: David Ortner Date: Fri, 5 Apr 2024 00:17:42 +0200 Subject: [PATCH 4/4] chore: [#1367] Adds integration tests for Bun --- .github/workflows/pull_request.yml | 8 ++++---- .github/workflows/release.yml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index a273b450..41e39121 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -21,6 +21,9 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'npm' + - name: Setup Bun + uses: oven-sh/setup-bun@v1 + - name: Cache node modules uses: actions/cache@v4 id: cache-node-modules @@ -48,12 +51,9 @@ jobs: path: .turbo key: turbo-master - - name: Install NPM dependencies + - name: Install dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' run: npm ci --ignore-scripts - - - name: Install Bun - run: curl -fsSL https://bun.sh/install | bash - name: Compile package run: npm run compile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9a15b1e4..966a9fef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,6 +69,9 @@ jobs: with: node-version: ${{ matrix.node-version }} + - name: Setup Bun + uses: oven-sh/setup-bun@v1 + - name: Cache node modules uses: actions/cache@v4 id: cache-node-modules @@ -88,13 +91,10 @@ jobs: restore-keys: | turbo-master - - name: Install NPM dependencies + - name: Install dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' run: npm ci --ignore-scripts - - name: Install Bun - run: curl -fsSL https://bun.sh/install | bash - - name: Set version run: node ./node_modules/.bin/happy-set-workspace-version --version=${{needs.check-next-version.outputs.next_version }}