From c17384b834e5ea390a4c19d47fb97770ac7eb7fa Mon Sep 17 00:00:00 2001 From: Ivan Hofer Date: Fri, 4 Feb 2022 17:04:39 +0100 Subject: [PATCH 01/12] improve types for `createEventDispatcher` --- src/runtime/internal/lifecycle.ts | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index bb3df3d2952..b188111ba28 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -27,12 +27,29 @@ export function onDestroy(fn: () => any) { get_current_component().$$.on_destroy.push(fn); } +type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void + ? I + : never + +type ExtractObjectValues> = Object[keyof Object] + +type ConstructDispatchFunction, EventKey extends keyof EventMap> = + EventMap[EventKey] extends never + ? (type: EventKey) => void + : (type: EventKey, detail: EventMap[EventKey]) => void + +type CreateDispatchFunctionMap = { + [Key in keyof EventMap]: ConstructDispatchFunction +} + +type EventDispatcher> = UnionToIntersection>> + export function createEventDispatcher< - EventMap extends {} = any ->(): >(type: EventKey, detail?: EventMap[EventKey]) => void { + EventMap extends Record = any +>(): EventDispatcher { const component = get_current_component(); - return (type: string, detail?: any) => { + return ((type: string, detail?: any) => { const callbacks = component.$$.callbacks[type]; if (callbacks) { @@ -43,7 +60,7 @@ export function createEventDispatcher< fn.call(component, event); }); } - }; + }) as EventDispatcher; } export function setContext(key, context: T) { @@ -59,7 +76,7 @@ export function getAllContexts = Map>(): T { } export function hasContext(key): boolean { - return get_current_component().$$.context.has(key); + return get_current_component().$$.context.has(key); } // TODO figure out if we still want to support From 22aa51d149acef0291a9ecd1dd6342aefa6b3c1d Mon Sep 17 00:00:00 2001 From: Ivan Hofer Date: Fri, 4 Feb 2022 19:25:15 +0100 Subject: [PATCH 02/12] allow optional parameters for `createEventDispatcher` --- src/runtime/internal/lifecycle.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index b188111ba28..faaeac2988e 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -36,6 +36,8 @@ type ExtractObjectValues> = Object[keyof Object] type ConstructDispatchFunction, EventKey extends keyof EventMap> = EventMap[EventKey] extends never ? (type: EventKey) => void + : undefined extends EventMap[EventKey] + ? (type: EventKey, detail?: EventMap[EventKey]) => void : (type: EventKey, detail: EventMap[EventKey]) => void type CreateDispatchFunctionMap = { From 54918719681fcff1ccb17faf889691efdef8dc38 Mon Sep 17 00:00:00 2001 From: Ivan Hofer Date: Sat, 5 Feb 2022 09:52:56 +0100 Subject: [PATCH 03/12] replace `undefined` with `null` --- src/runtime/internal/lifecycle.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index faaeac2988e..4eacb698d62 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -34,9 +34,9 @@ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( type ExtractObjectValues> = Object[keyof Object] type ConstructDispatchFunction, EventKey extends keyof EventMap> = - EventMap[EventKey] extends never + EventMap[EventKey] extends never | null ? (type: EventKey) => void - : undefined extends EventMap[EventKey] + : null extends EventMap[EventKey] ? (type: EventKey, detail?: EventMap[EventKey]) => void : (type: EventKey, detail: EventMap[EventKey]) => void From f50219a55eb7ee18eda6ce88fe8709cf36632101 Mon Sep 17 00:00:00 2001 From: Ivan Hofer Date: Wed, 22 Feb 2023 19:36:17 +0100 Subject: [PATCH 04/12] revert formatting changes --- src/runtime/internal/lifecycle.ts | 66 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index 4b0252d2660..fd03779fa82 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -13,9 +13,9 @@ export function get_current_component() { /** * Schedules a callback to run immediately before the component is updated after any state change. - * + * * The first time the callback runs will be before the initial `onMount` - * + * * https://svelte.dev/docs#run-time-svelte-beforeupdate */ export function beforeUpdate(fn: () => any) { @@ -23,12 +23,12 @@ export function beforeUpdate(fn: () => any) { } /** - * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. - * It must be called during the component's initialisation (but doesn't need to live *inside* the component; + * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. + * It must be called during the component's initialisation (but doesn't need to live *inside* the component; * it can be called from an external module). - * + * * `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api). - * + * * https://svelte.dev/docs#run-time-svelte-onmount */ export function onMount(fn: () => any) { @@ -37,19 +37,19 @@ export function onMount(fn: () => any) { /** * Schedules a callback to run immediately after the component has been updated. - * - * The first time the callback runs will be after the initial `onMount` + * + * The first time the callback runs will be after the initial `onMount` */ export function afterUpdate(fn: () => any) { get_current_component().$$.after_update.push(fn); } -/** +/** * Schedules a callback to run immediately before the component is unmounted. - * - * Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the + * + * Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the * only one that runs inside a server-side component. - * + * * https://svelte.dev/docs#run-time-svelte-ondestroy */ export function onDestroy(fn: () => any) { @@ -80,15 +80,15 @@ export interface DispatchOptions { } /** - * Creates an event dispatcher that can be used to dispatch [component events](/docs#template-syntax-component-directives-on-eventname). + * Creates an event dispatcher that can be used to dispatch [component events](/docs#template-syntax-component-directives-on-eventname). * Event dispatchers are functions that can take two arguments: `name` and `detail`. - * - * Component events created with `createEventDispatcher` create a - * [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). + * + * Component events created with `createEventDispatcher` create a + * [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). * These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). - * The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) - * property and can contain any type of data. - * + * The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) + * property and can contain any type of data. + * * https://svelte.dev/docs#run-time-svelte-createeventdispatcher */ export function createEventDispatcher< @@ -114,12 +114,12 @@ export function createEventDispatcher< } /** - * Associates an arbitrary `context` object with the current component and the specified `key` - * and returns that object. The context is then available to children of the component + * Associates an arbitrary `context` object with the current component and the specified `key` + * and returns that object. The context is then available to children of the component * (including slotted content) with `getContext`. - * - * Like lifecycle functions, this must be called during component initialisation. - * + * + * Like lifecycle functions, this must be called during component initialisation. + * * https://svelte.dev/docs#run-time-svelte-setcontext */ export function setContext(key, context: T): T { @@ -128,9 +128,9 @@ export function setContext(key, context: T): T { } /** - * Retrieves the context that belongs to the closest parent component with the specified `key`. - * Must be called during component initialisation. - * + * Retrieves the context that belongs to the closest parent component with the specified `key`. + * Must be called during component initialisation. + * * https://svelte.dev/docs#run-time-svelte-getcontext */ export function getContext(key): T { @@ -138,10 +138,10 @@ export function getContext(key): T { } /** - * Retrieves the whole context map that belongs to the closest parent component. - * Must be called during component initialisation. Useful, for example, if you + * Retrieves the whole context map that belongs to the closest parent component. + * Must be called during component initialisation. Useful, for example, if you * programmatically create a component and want to pass the existing context to it. - * + * * https://svelte.dev/docs#run-time-svelte-getallcontexts */ export function getAllContexts = Map>(): T { @@ -149,9 +149,9 @@ export function getAllContexts = Map>(): T { } /** - * Checks whether a given `key` has been set in the context of a parent component. - * Must be called during component initialisation. - * + * Checks whether a given `key` has been set in the context of a parent component. + * Must be called during component initialisation. + * * https://svelte.dev/docs#run-time-svelte-hascontext */ export function hasContext(key): boolean { From ba35faea5098ab5239923234ec2dfef10a3f111c Mon Sep 17 00:00:00 2001 From: Ivan Hofer Date: Wed, 22 Feb 2023 19:43:41 +0100 Subject: [PATCH 05/12] fix types --- src/runtime/internal/lifecycle.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index fd03779fa82..7fe8c0c57e7 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -64,10 +64,10 @@ type ExtractObjectValues> = Object[keyof Object] type ConstructDispatchFunction, EventKey extends keyof EventMap> = EventMap[EventKey] extends never | null - ? (type: EventKey) => void + ? (type: EventKey) => boolean : null extends EventMap[EventKey] - ? (type: EventKey, detail?: EventMap[EventKey]) => void - : (type: EventKey, detail: EventMap[EventKey]) => void + ? (type: EventKey, detail?: EventMap[EventKey], options?: DispatchOptions) => boolean + : (type: EventKey, detail: EventMap[EventKey], options?: DispatchOptions) => boolean type CreateDispatchFunctionMap = { [Key in keyof EventMap]: ConstructDispatchFunction @@ -91,12 +91,10 @@ export interface DispatchOptions { * * https://svelte.dev/docs#run-time-svelte-createeventdispatcher */ -export function createEventDispatcher< - EventMap extends Record = any ->(): EventDispatcher { +export function createEventDispatcher = any>(): EventDispatcher { const component = get_current_component(); - return (type: string, detail?: any, { cancelable = false } = {}): boolean => { + return ((type: string, detail?: any, { cancelable = false } = {}): boolean => { const callbacks = component.$$.callbacks[type]; if (callbacks) { @@ -110,7 +108,7 @@ export function createEventDispatcher< } return true; - }; + }) as EventDispatcher; } /** @@ -155,7 +153,7 @@ export function getAllContexts = Map>(): T { * https://svelte.dev/docs#run-time-svelte-hascontext */ export function hasContext(key): boolean { - return get_current_component().$$.context.has(key); + return get_current_component().$$.context.has(key); } // TODO figure out if we still want to support From 28c3508fb7323cfd42cb421dc20eb9f407f42f5a Mon Sep 17 00:00:00 2001 From: Ivan Hofer Date: Wed, 22 Feb 2023 19:54:30 +0100 Subject: [PATCH 06/12] fix no payload case --- src/runtime/internal/lifecycle.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index 7fe8c0c57e7..aad65743841 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -64,7 +64,7 @@ type ExtractObjectValues> = Object[keyof Object] type ConstructDispatchFunction, EventKey extends keyof EventMap> = EventMap[EventKey] extends never | null - ? (type: EventKey) => boolean + ? (type: EventKey, detail?: null, options?: DispatchOptions) => boolean : null extends EventMap[EventKey] ? (type: EventKey, detail?: EventMap[EventKey], options?: DispatchOptions) => boolean : (type: EventKey, detail: EventMap[EventKey], options?: DispatchOptions) => boolean @@ -111,6 +111,16 @@ export function createEventDispatcher = any }) as EventDispatcher; } +// no type restrictions +const dispatch1 = createEventDispatcher() +dispatch1('click') +dispatch1('something') +dispatch1('something', true) + +const dispatch2 = createEventDispatcher<{ click: never }>() +dispatch2('click') +dispatch2('click', true) + /** * Associates an arbitrary `context` object with the current component and the specified `key` * and returns that object. The context is then available to children of the component @@ -153,7 +163,7 @@ export function getAllContexts = Map>(): T { * https://svelte.dev/docs#run-time-svelte-hascontext */ export function hasContext(key): boolean { - return get_current_component().$$.context.has(key); + return get_current_component().$$.context.has(key); } // TODO figure out if we still want to support From cd24b6a77bc2516300d26e2a34576f9c3dc11c45 Mon Sep 17 00:00:00 2001 From: Ivan Hofer Date: Wed, 22 Feb 2023 20:00:03 +0100 Subject: [PATCH 07/12] remove test code --- src/runtime/internal/lifecycle.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index aad65743841..5e34da05e36 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -111,16 +111,6 @@ export function createEventDispatcher = any }) as EventDispatcher; } -// no type restrictions -const dispatch1 = createEventDispatcher() -dispatch1('click') -dispatch1('something') -dispatch1('something', true) - -const dispatch2 = createEventDispatcher<{ click: never }>() -dispatch2('click') -dispatch2('click', true) - /** * Associates an arbitrary `context` object with the current component and the specified `key` * and returns that object. The context is then available to children of the component From 24258516980b098f57f8ef2d6936c200afd667af Mon Sep 17 00:00:00 2001 From: Ivan Hofer Date: Wed, 22 Feb 2023 20:01:27 +0100 Subject: [PATCH 08/12] revert formatting changes --- src/runtime/internal/lifecycle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index 5e34da05e36..df8413d793f 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -153,7 +153,7 @@ export function getAllContexts = Map>(): T { * https://svelte.dev/docs#run-time-svelte-hascontext */ export function hasContext(key): boolean { - return get_current_component().$$.context.has(key); + return get_current_component().$$.context.has(key); } // TODO figure out if we still want to support From 234f9f65749e46e9a2937d17cc476b42c46c2d30 Mon Sep 17 00:00:00 2001 From: Ivan Hofer Date: Wed, 22 Feb 2023 20:02:34 +0100 Subject: [PATCH 09/12] revert formatting changes --- src/runtime/internal/lifecycle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index df8413d793f..fb4048f52f1 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -153,7 +153,7 @@ export function getAllContexts = Map>(): T { * https://svelte.dev/docs#run-time-svelte-hascontext */ export function hasContext(key): boolean { - return get_current_component().$$.context.has(key); + return get_current_component().$$.context.has(key); } // TODO figure out if we still want to support From 8d5767497e1801c8b2cc9cd21d89b72743918cef Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 14 Apr 2023 11:44:07 +0200 Subject: [PATCH 10/12] simplify type, add tests --- package.json | 2 +- src/runtime/internal/lifecycle.ts | 34 ++++++++++----------- test/types/create-event-dispatcher.ts | 43 +++++++++++++++++++++++++++ test/types/tsconfig.json | 16 ++++++++++ 4 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 test/types/create-event-dispatcher.ts create mode 100644 test/types/tsconfig.json diff --git a/package.json b/package.json index c5239c8b502..8b69a2f15ee 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ }, "types": "types/runtime/index.d.ts", "scripts": { - "test": "npm run test:unit && npm run test:integration", + "test": "npm run test:unit && npm run test:integration && ECHO \"manually check that there are no type errors in test/types by opening the files in there\"", "test:integration": "mocha --exit", "test:unit": "mocha --config .mocharc.unit.js --exit", "quicktest": "mocha --exit", diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index fb4048f52f1..7592e72a386 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -56,25 +56,14 @@ export function onDestroy(fn: () => any) { get_current_component().$$.on_destroy.push(fn); } -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void - ? I - : never - -type ExtractObjectValues> = Object[keyof Object] - -type ConstructDispatchFunction, EventKey extends keyof EventMap> = - EventMap[EventKey] extends never | null - ? (type: EventKey, detail?: null, options?: DispatchOptions) => boolean - : null extends EventMap[EventKey] - ? (type: EventKey, detail?: EventMap[EventKey], options?: DispatchOptions) => boolean - : (type: EventKey, detail: EventMap[EventKey], options?: DispatchOptions) => boolean - -type CreateDispatchFunctionMap = { - [Key in keyof EventMap]: ConstructDispatchFunction +export interface EventDispatcher> { + ( + ...args: [EventMap[Type]] extends [never] ? [type: Type, parameter?: null | undefined, options?: DispatchOptions] : + null extends EventMap[Type] ? [type: Type, parameter?: EventMap[Type], options?: DispatchOptions] : + undefined extends EventMap[Type] ? [type: Type, parameter?: EventMap[Type], options?: DispatchOptions] : + [type: Type, parameter: EventMap[Type], options?: DispatchOptions]): boolean; } -type EventDispatcher> = UnionToIntersection>> - export interface DispatchOptions { cancelable?: boolean; } @@ -87,7 +76,16 @@ export interface DispatchOptions { * [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). * These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). * The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) - * property and can contain any type of data. + * property and can contain any type of data. + * + * The event dispatcher can be typed to narrow the allowed event names and the type of the `detail` argument: + * ```ts + * const dispatch = createEventDispatcher<{ + * loaded: never; // does not take a detail argument + * change: string; // takes a detail argument of type string, which is required + * optional: number | null; // takes an optional detail argument of type number + * }>(); + * ``` * * https://svelte.dev/docs#run-time-svelte-createeventdispatcher */ diff --git a/test/types/create-event-dispatcher.ts b/test/types/create-event-dispatcher.ts new file mode 100644 index 00000000000..d9fc6c65bdc --- /dev/null +++ b/test/types/create-event-dispatcher.ts @@ -0,0 +1,43 @@ +import { createEventDispatcher } from '$runtime/internal/lifecycle'; + +const dispatch = createEventDispatcher<{ + loaded: never + change: string + valid: boolean + optional: number | null +}>(); + +// @ts-expect-error: dispatch invalid event +dispatch('some-event'); + +dispatch('loaded'); +dispatch('loaded', null); +dispatch('loaded', undefined); +dispatch('loaded', undefined, { cancelable: true }); +// @ts-expect-error: no detail accepted +dispatch('loaded', 123); + +// @ts-expect-error: detail not provided +dispatch('change'); +dispatch('change', 'string'); +dispatch('change', 'string', { cancelable: true }); +// @ts-expect-error: wrong type of detail +dispatch('change', 123); +// @ts-expect-error: wrong type of detail +dispatch('change', undefined); + +dispatch('valid', true); +dispatch('valid', true, { cancelable: true }); +// @ts-expect-error: wrong type of detail +dispatch('valid', 'string'); + +dispatch('optional'); +dispatch('optional', 123); +dispatch('optional', 123, { cancelable: true }); +dispatch('optional', null); +dispatch('optional', undefined); +dispatch('optional', undefined, { cancelable: true }); +// @ts-expect-error: wrong type of optional detail +dispatch('optional', 'string'); +// @ts-expect-error: wrong type of option +dispatch('optional', undefined, { cancelabled: true }); diff --git a/test/types/tsconfig.json b/test/types/tsconfig.json new file mode 100644 index 00000000000..108ed2a2b29 --- /dev/null +++ b/test/types/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "../..", + "baseUrl": "../../", + "paths": { + "$runtime/*": ["src/runtime/*"] + }, + // enable strictest options + "allowUnreachableCode": false, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "strict": true, + }, + "include": ["."] +} \ No newline at end of file From 4f795e9491dab0e189f31d622d58a301354cc2c6 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 14 Apr 2023 12:07:27 +0200 Subject: [PATCH 11/12] changelog --- CHANGELOG.md | 4 +++- package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44b50c6951d..a6345d99b31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ ## Unreleased (4.0) -* Minimum supported Node version is now Node 14 +* **breaking** Minimum supported Node version is now Node 14 +* **breaking** Minimum supported TypeScript version is now 5 (it will likely work with lower versions, but we make no guarantess about that) +* **breaking** Stricter types for `createEventDispatcher` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224)) ## Unreleased (3.0) diff --git a/package.json b/package.json index 8b69a2f15ee..dc27fa20846 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ }, "types": "types/runtime/index.d.ts", "scripts": { - "test": "npm run test:unit && npm run test:integration && ECHO \"manually check that there are no type errors in test/types by opening the files in there\"", + "test": "npm run test:unit && npm run test:integration && echo \"manually check that there are no type errors in test/types by opening the files in there\"", "test:integration": "mocha --exit", "test:unit": "mocha --config .mocharc.unit.js --exit", "quicktest": "mocha --exit", From 04118511c555c4bd35ea7236d191e015b248f78b Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 14 Apr 2023 12:20:49 +0200 Subject: [PATCH 12/12] fix ci --- .github/workflows/ci.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9288f8333c0..8520c043103 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,8 +46,17 @@ jobs: timeout-minutes: 10 strategy: matrix: - node-version: 14 - os: [ubuntu-latest, windows-latest, macOS-latest] + include: + - node-version: 14 + os: ubuntu-latest + - node-version: 14 + os: windows-latest + - node-version: 14 + os: macOS-latest + - node-version: 16 + os: ubuntu-latest + - node-version: 18 + os: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3