Skip to content

Commit 99ebbfc

Browse files
authored
BREAKING CHANGE: [#1154] Makes the types for Happy DOM strict (#1842)
1 parent a94d780 commit 99ebbfc

File tree

238 files changed

+3735
-3392
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

238 files changed

+3735
-3392
lines changed

package-lock.json

Lines changed: 1033 additions & 1332 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"semver": "^7.3.5",
3434
"turbo": "^1.11.3",
3535
"typescript": "^5.8.3",
36-
"vitest": "^2.1.9"
36+
"vitest": "^3.2.2"
3737
},
3838
"happyLintChanged": {
3939
"rules": [

packages/global-registrator/package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,10 @@
7676
"test:bun": "bun test ./test/bun/Bun.test.js"
7777
},
7878
"dependencies": {
79-
"happy-dom": "^0.0.0"
79+
"happy-dom": "^0.0.0",
80+
"@types/node": "^20.0.0"
8081
},
8182
"devDependencies": {
82-
"@typescript-eslint/eslint-plugin": "^5.16.0",
83-
"@typescript-eslint/parser": "^5.16.0",
84-
"@types/node": "^20.0.0",
8583
"typescript": "^5.8.3",
8684
"lit": "^3.3.0",
8785
"react": "^18.2.0",

packages/global-registrator/src/GlobalRegistrator.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const IGNORE_LIST = ['constructor', 'undefined', 'NaN', 'global', 'globalThis'];
77
*
88
*/
99
export default class GlobalRegistrator {
10-
static #registered: { [key: string | symbol]: PropertyDescriptor } | null = null;
10+
static #registered: { [key: string | symbol]: PropertyDescriptor | null } | null = null;
1111

1212
/**
1313
* Returns the registered state.
@@ -57,7 +57,7 @@ export default class GlobalRegistrator {
5757

5858
// If the property is the window object, replace it with the global object
5959
if (windowPropertyDescriptor.value === window) {
60-
window[key] = globalThis;
60+
(<any>window)[key] = globalThis;
6161
windowPropertyDescriptor.value = globalThis;
6262
}
6363

@@ -77,9 +77,9 @@ export default class GlobalRegistrator {
7777
this.#registered[key] = null;
7878

7979
// If the property is the window object, replace it with the global object
80-
if (propertyDescriptor.value === window) {
81-
window[key] = globalThis;
82-
propertyDescriptor.value = globalThis;
80+
if (propertyDescriptor!.value === window) {
81+
(<any>window)[key] = globalThis;
82+
propertyDescriptor!.value = globalThis;
8383
}
8484

8585
Object.defineProperty(globalThis, key, {
@@ -89,7 +89,7 @@ export default class GlobalRegistrator {
8989
}
9090

9191
// Set owner window on document to global
92-
globalThis.document[PropertySymbol.defaultView] = globalThis;
92+
(<any>globalThis).document[PropertySymbol.defaultView] = globalThis;
9393
}
9494

9595
/**
@@ -102,13 +102,13 @@ export default class GlobalRegistrator {
102102
);
103103
}
104104

105-
const happyDOM = globalThis.happyDOM;
105+
const happyDOM = (<any>globalThis).happyDOM;
106106

107107
for (const key of Object.keys(this.#registered)) {
108108
if (this.#registered[key] !== null) {
109109
Object.defineProperty(globalThis, key, this.#registered[key]);
110110
} else {
111-
delete globalThis[key];
111+
delete (<any>globalThis)[key];
112112
}
113113
}
114114

packages/global-registrator/tsconfig.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
"resolveJsonModule": true,
1414
"noUnusedLocals": true,
1515
"noUnusedParameters": true,
16-
"removeComments": false,
16+
"noImplicitReturns": false,
17+
"noImplicitAny" : true,
18+
"removeComments": false,
1719
"preserveConstEnums": true,
20+
"strict": true,
1821
"sourceMap": true,
1922
"skipLibCheck": true,
2023
"baseUrl": ".",

packages/happy-dom/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,15 @@
7777
"test:debug": "vitest run --inspect-brk --no-file-parallelism"
7878
},
7979
"dependencies": {
80-
"webidl-conversions": "^7.0.0",
81-
"whatwg-mimetype": "^3.0.0"
80+
"whatwg-mimetype": "^3.0.0",
81+
"@types/whatwg-mimetype": "^3.0.2",
82+
"@types/node": "^20.0.0"
8283
},
8384
"devDependencies": {
84-
"@types/node": "^20.0.0",
85-
"@vitest/ui": "^2.1.4",
85+
"@vitest/ui": "^3.2.3",
8686
"@webref/css": "6.6.2",
8787
"typescript": "^5.8.3",
88-
"vitest": "^2.1.9"
88+
"vitest": "^3.2.2"
8989
},
9090
"engines": {
9191
"node": ">=20.0.0"

packages/happy-dom/src/async-task-manager/AsyncTaskManager.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export default class AsyncTaskManager {
2424
}> = [];
2525
private aborted = false;
2626
private destroyed = false;
27+
#debugTimeout: NodeJS.Timeout | null = null;
2728
#browserFrame: IBrowserFrame;
28-
#debugTimeout: NodeJS.Timeout | null;
2929

3030
/**
3131
* Constructor.
@@ -89,8 +89,8 @@ export default class AsyncTaskManager {
8989
this.waitUntilCompleteTimer = null;
9090
}
9191
this.runningTimers.push(timerID);
92-
if (this.#browserFrame.page?.context?.browser?.settings?.debug?.traceWaitUntilComplete > 0) {
93-
this.debugTrace.set(timerID, new Error().stack);
92+
if (this.#browserFrame.page.context.browser.settings.debug.traceWaitUntilComplete > 0) {
93+
this.debugTrace.set(timerID, new Error().stack!);
9494
}
9595
}
9696

@@ -109,7 +109,7 @@ export default class AsyncTaskManager {
109109
this.runningTimers.splice(index, 1);
110110
this.resolveWhenComplete();
111111
}
112-
if (this.#browserFrame.page?.context?.browser?.settings?.debug?.traceWaitUntilComplete > 0) {
112+
if (this.#browserFrame.page.context.browser.settings.debug.traceWaitUntilComplete > 0) {
113113
this.debugTrace.delete(timerID);
114114
}
115115
}
@@ -129,8 +129,8 @@ export default class AsyncTaskManager {
129129
this.waitUntilCompleteTimer = null;
130130
}
131131
this.runningImmediates.push(immediateID);
132-
if (this.#browserFrame.page?.context?.browser?.settings?.debug?.traceWaitUntilComplete > 0) {
133-
this.debugTrace.set(immediateID, new Error().stack);
132+
if (this.#browserFrame.page.context.browser.settings.debug.traceWaitUntilComplete > 0) {
133+
this.debugTrace.set(immediateID, new Error().stack!);
134134
}
135135
}
136136

@@ -149,7 +149,7 @@ export default class AsyncTaskManager {
149149
this.runningImmediates.splice(index, 1);
150150
this.resolveWhenComplete();
151151
}
152-
if (this.#browserFrame.page?.context?.browser?.settings?.debug?.traceWaitUntilComplete > 0) {
152+
if (this.#browserFrame.page.context.browser.settings.debug.traceWaitUntilComplete > 0) {
153153
this.debugTrace.delete(immediateID);
154154
}
155155
}
@@ -176,8 +176,8 @@ export default class AsyncTaskManager {
176176
const taskID = this.newTaskID();
177177
this.runningTasks[taskID] = abortHandler ? abortHandler : () => {};
178178
this.runningTaskCount++;
179-
if (this.#browserFrame.page?.context?.browser?.settings?.debug?.traceWaitUntilComplete > 0) {
180-
this.debugTrace.set(taskID, new Error().stack);
179+
if (this.#browserFrame.page.context.browser.settings.debug.traceWaitUntilComplete > 0) {
180+
this.debugTrace.set(taskID, new Error().stack!);
181181
}
182182
return taskID;
183183
}
@@ -196,7 +196,7 @@ export default class AsyncTaskManager {
196196
this.runningTaskCount--;
197197
this.resolveWhenComplete();
198198
}
199-
if (this.#browserFrame.page?.context?.browser?.settings?.debug?.traceWaitUntilComplete > 0) {
199+
if (this.#browserFrame.page.context.browser.settings.debug.traceWaitUntilComplete > 0) {
200200
this.debugTrace.delete(taskID);
201201
}
202202
}
@@ -260,7 +260,7 @@ export default class AsyncTaskManager {
260260
* Applies debugging.
261261
*/
262262
private applyDebugging(): void {
263-
const debug = this.#browserFrame.page?.context?.browser?.settings?.debug;
263+
const debug = this.#browserFrame.page.context.browser.settings.debug;
264264
if (!debug?.traceWaitUntilComplete || debug.traceWaitUntilComplete < 1) {
265265
return;
266266
}

packages/happy-dom/src/browser/Browser.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ export default class Browser implements IBrowser {
3333
this.contexts = [new BrowserContext(this)];
3434
}
3535

36+
/**
37+
* Returns true if the browser is closed.
38+
*
39+
* @returns True if the browser is closed.
40+
*/
41+
public get closed(): boolean {
42+
return this.contexts.length === 0;
43+
}
44+
3645
/**
3746
* Returns the default context.
3847
*
@@ -49,8 +58,12 @@ export default class Browser implements IBrowser {
4958
* Aborts all ongoing operations and destroys the browser.
5059
*/
5160
public async close(): Promise<void> {
52-
await Promise.all(this.contexts.slice().map((context) => context.close()));
61+
if (this.contexts.length === 0) {
62+
return;
63+
}
64+
const contexts = this.contexts;
5365
(<BrowserContext[]>this.contexts) = [];
66+
await Promise.all(contexts.map((context) => context.close()));
5467
}
5568

5669
/**

packages/happy-dom/src/browser/BrowserContext.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export default class BrowserContext implements IBrowserContext {
1717
public readonly cookieContainer: ICookieContainer = new CookieContainer();
1818
public readonly responseCache: IResponseCache = new ResponseCache();
1919
public readonly preflightResponseCache: IPreflightResponseCache = new PreflightResponseCache();
20+
public readonly closed: boolean = false;
2021

2122
/**
2223
* Constructor.
@@ -31,25 +32,31 @@ export default class BrowserContext implements IBrowserContext {
3132
* Aborts all ongoing operations and destroys the context.
3233
*/
3334
public async close(): Promise<void> {
34-
if (!this.browser) {
35+
if (this.closed) {
3536
return;
3637
}
38+
39+
if (this.browser.contexts[0] === this) {
40+
throw new Error(
41+
'Cannot close the default context. Use `browser.close()` to close the browser instead.'
42+
);
43+
}
44+
45+
(<boolean>this.closed) = true;
46+
3747
await Promise.all(this.pages.slice().map((page) => page.close()));
48+
3849
const browser = this.browser;
3950
const index = browser.contexts.indexOf(this);
51+
4052
if (index !== -1) {
4153
browser.contexts.splice(index, 1);
4254
}
55+
4356
(<BrowserPage[]>this.pages) = [];
44-
(<Browser | null>this.browser) = null;
45-
(<ICookieContainer | null>this.cookieContainer) = null;
57+
this.cookieContainer.clearCookies();
4658
this.responseCache.clear();
4759
this.preflightResponseCache.clear();
48-
(<ResponseCache | null>this.responseCache) = null;
49-
(<PreflightResponseCache | null>this.preflightResponseCache) = null;
50-
if (browser.contexts.length === 0) {
51-
browser.close();
52-
}
5360
}
5461

5562
/**

packages/happy-dom/src/browser/BrowserFrame.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ export default class BrowserFrame implements IBrowserFrame {
2323
public readonly parentFrame: BrowserFrame | null = null;
2424
public readonly page: BrowserPage;
2525
public readonly window: BrowserWindow;
26-
public [PropertySymbol.asyncTaskManager] = new AsyncTaskManager(this);
26+
public readonly closed: boolean = false;
27+
public [PropertySymbol.asyncTaskManager]: AsyncTaskManager = new AsyncTaskManager(this);
2728
public [PropertySymbol.listeners]: { navigation: Array<() => void> } = { navigation: [] };
2829
public [PropertySymbol.openerFrame]: IBrowserFrame | null = null;
2930
public [PropertySymbol.openerWindow]: BrowserWindow | CrossOriginBrowserWindow | null = null;

0 commit comments

Comments
 (0)