diff --git a/docs/framework/react/reference/useTimer.md b/docs/framework/react/reference/useTimer.md
new file mode 100644
index 00000000..d78becbb
--- /dev/null
+++ b/docs/framework/react/reference/useTimer.md
@@ -0,0 +1,76 @@
+---
+title: Use Timer
+id: useTimer
+---
+
+### useTimer
+
+```ts
+export function useTimer({
+ initialTime: number,
+ onFinished?: () => void,
+ timeZone?: Temporal.TimeZoneLike,
+ onStart?: () => void,
+ onStop?: () => void,
+ onReset?: () => void,
+}): TimerApi;
+```
+
+`useTimer` is a hook that provides a comprehensive set of functionalities for managing a countdown timer, including starting, stopping, and resetting the timer. It also includes the ability to trigger a callback when the timer finishes, starts, stops, or resets.
+
+
+#### Parameters
+
+- `initialTime: number`
+The initial time for the timer, specified in seconds.
+- `timeZone?: Temporal.TimeZoneLike`
+Optional time zone specification for the timer. Defaults to the system's time zone.
+- `onFinished?: () => void`
+An optional callback function that is called when the timer finishes.
+- `onStart?: () => void`
+Optional callback function that is called when the timer starts.
+- `onStop?: () => void`
+Optional callback function that is called when the timer stops.
+- `onReset?: () => void`
+Optional callback function that is called when the timer resets.
+
+
+#### Returns
+
+- `remainingTime: number`
+This value represents the remaining time of the timer in seconds.
+- `isRunning: boolean`
+This value represents whether the timer is currently running.
+- `start: () => void`
+This function starts the timer.
+- `stop: () => void`
+This function stops the timer.
+- `reset: () => void`
+This function resets the timer to the initial time.
+
+
+#### Example Usage
+
+```ts
+import { useTimer } from '@tanstack/react-time';
+
+const TimerComponent = () => {
+ const { remainingTime, isRunning, start, stop, reset } = useTimer({
+ initialTime: 60,
+ onFinished: () => {
+ console.log('Timer finished!');
+ },
+ timeZone: 'America/New_York',
+ });
+
+ return (
+
+
Remaining Time: {remainingTime}
+
Is Running: {isRunning ? 'Yes' : 'No'}
+
Start
+
Stop
+
Reset
+
+ );
+};
+```
diff --git a/docs/reference/timer.md b/docs/reference/timer.md
new file mode 100644
index 00000000..3002222b
--- /dev/null
+++ b/docs/reference/timer.md
@@ -0,0 +1,58 @@
+---
+title: Timer
+id: timer
+---
+
+# Timer
+
+```ts
+export class Timer extends TimeCore implements TimerActions {
+ constructor(options: TimerOptions);
+}
+```
+
+The Timer class provides functionality for managing a countdown timer, including starting, stopping, and resetting the timer. It also includes the ability to trigger a callback when the timer finishes.
+
+
+## Parameters
+
+- `initialTime: number`
+The initial time for the timer, specified in seconds.
+- `onFinished?: () => void`
+An optional callback function that is called when the timer finishes.
+- `timeZone?: Temporal.TimeZoneLike`
+Optional time zone specification for the timer. Defaults to the system's time zone.
+- `onStart?: () => void`
+Optional callback function that is called when the timer starts.
+- `onStop?: () => void`
+Optional callback function that is called when the timer stops.
+- `onReset?: () => void`
+Optional callback function that is called when the timer resets.
+
+
+## Methods
+
+- `start(): void`
+Starts the timer.
+- `stop(): void`
+Stops the timer.
+- `reset(): void`
+Resets the timer to the initial time.
+
+
+## Example Usage
+
+```ts
+import { Timer } from '@tanstack/time';
+
+const timer = new Timer({
+ initialTime: 60, // 60 seconds
+ onFinished: () => {
+ console.log('Timer finished!');
+ },
+ timeZone: 'America/New_York',
+});
+
+// Start the timer
+timer.start();
+```
\ No newline at end of file
diff --git a/packages/react-time/package.json b/packages/react-time/package.json
index c3f9d136..32a3f06b 100644
--- a/packages/react-time/package.json
+++ b/packages/react-time/package.json
@@ -62,6 +62,7 @@
"react-dom": "^17.0.0 || ^18.0.0"
},
"dependencies": {
+ "@tanstack/react-store": "^0.5.2",
"@tanstack/time": "workspace:*",
"use-sync-external-store": "^1.2.0"
},
diff --git a/packages/react-time/src/tests/useTimer.test.ts b/packages/react-time/src/tests/useTimer.test.ts
new file mode 100644
index 00000000..e8649f90
--- /dev/null
+++ b/packages/react-time/src/tests/useTimer.test.ts
@@ -0,0 +1,99 @@
+import { beforeEach, describe, expect, test, vi } from 'vitest'
+import { act, renderHook } from '@testing-library/react'
+import { useTimer } from '../useTimer'
+
+describe('useTimer', () => {
+ beforeEach(() => {
+ vi.useFakeTimers()
+ })
+
+ test('should start the timer', () => {
+ const { result } = renderHook(() => useTimer({ initialTime: 5 }))
+ act(() => {
+ result.current.start()
+ })
+ expect(result.current.isRunning).toBe(true)
+ })
+
+ test('should stop the timer', () => {
+ const { result } = renderHook(() => useTimer({ initialTime: 5 }))
+ act(() => {
+ result.current.start()
+ })
+ act(() => {
+ result.current.stop()
+ })
+ expect(result.current.isRunning).toBe(false)
+ })
+
+ test('should reset the timer', () => {
+ const { result } = renderHook(() => useTimer({ initialTime: 5 }))
+ act(() => {
+ result.current.start()
+ })
+ act(() => {
+ result.current.stop()
+ })
+ expect(result.current.isRunning).toBe(false)
+ expect(result.current.remainingTime).toBe(5)
+ })
+
+ test('should update the remaining time', () => {
+ const { result } = renderHook(() => useTimer({ initialTime: 5 }))
+ act(() => {
+ result.current.start()
+ })
+ act(() => {
+ vi.advanceTimersByTime(1000)
+ })
+ expect(result.current.remainingTime).toBe(4)
+ })
+
+ test('should call onStart callback', () => {
+ const onStart = vi.fn()
+ const { result } = renderHook(() => useTimer({ initialTime: 5, onStart }))
+ act(() => {
+ result.current.start()
+ })
+ expect(onStart).toHaveBeenCalledTimes(1)
+ })
+
+ test('should call onStop callback', () => {
+ const onStop = vi.fn()
+ const { result } = renderHook(() => useTimer({ initialTime: 5, onStop }))
+ act(() => {
+ result.current.start()
+ })
+ act(() => {
+ result.current.stop()
+ })
+ expect(onStop).toHaveBeenCalledTimes(1)
+ })
+
+ test('should call onReset callback', () => {
+ const onReset = vi.fn()
+ const { result } = renderHook(() => useTimer({ initialTime: 5, onReset }))
+ act(() => {
+ result.current.start()
+ })
+ act(() => {
+ result.current.stop()
+ })
+ act(() => {
+ result.current.reset()
+ })
+ expect(onReset).toHaveBeenCalledTimes(1)
+ })
+
+ test('should call onFinish callback', () => {
+ const onFinish = vi.fn()
+ const { result } = renderHook(() => useTimer({ initialTime: 5, onFinish }))
+ act(() => {
+ result.current.start()
+ })
+ act(() => {
+ vi.advanceTimersByTime(5000)
+ })
+ expect(onFinish).toHaveBeenCalledTimes(1)
+ })
+})
diff --git a/packages/react-time/src/useTimer.ts b/packages/react-time/src/useTimer.ts
new file mode 100644
index 00000000..71ba97c8
--- /dev/null
+++ b/packages/react-time/src/useTimer.ts
@@ -0,0 +1,22 @@
+import { useStore } from '@tanstack/react-store'
+import { Timer, type TimerApi, type TimerOptions } from '@tanstack/time'
+import { useCallback, useState } from 'react'
+
+export const useTimer = (options: TimerOptions): TimerApi => {
+ const [timer] = useState(() => new Timer(options))
+ const state = useStore(timer.store)
+
+ const start = useCallback(() => {
+ timer.start()
+ }, [timer])
+
+ const stop = useCallback(() => {
+ timer.stop()
+ }, [timer])
+
+ const reset = useCallback(() => {
+ timer.reset()
+ }, [timer])
+
+ return { ...state, start, stop, reset }
+}
diff --git a/packages/time/package.json b/packages/time/package.json
index 081903be..44f58127 100644
--- a/packages/time/package.json
+++ b/packages/time/package.json
@@ -55,5 +55,12 @@
"files": [
"dist",
"src"
- ]
+ ],
+ "dependencies": {
+ "@js-temporal/polyfill": "^0.4.4",
+ "@tanstack/store": "^0.4.1"
+ },
+ "devDependencies": {
+ "csstype": "^3.1.3"
+ }
}
diff --git a/packages/time/src/core/index.ts b/packages/time/src/core/index.ts
new file mode 100644
index 00000000..46213912
--- /dev/null
+++ b/packages/time/src/core/index.ts
@@ -0,0 +1 @@
+export * from './timer'
diff --git a/packages/time/src/core/time.ts b/packages/time/src/core/time.ts
new file mode 100644
index 00000000..75ee64bb
--- /dev/null
+++ b/packages/time/src/core/time.ts
@@ -0,0 +1,60 @@
+import { Temporal } from '@js-temporal/polyfill'
+import { Store } from '@tanstack/store'
+import { getDefaultTimeZone } from '../utils/dateDefaults'
+
+export interface TimeCoreOptions {
+ /**
+ * The time zone to use for the current time.
+ * @default Intl.DateTimeFormat().resolvedOptions().timeZone
+ */
+ timeZone?: Temporal.TimeZoneLike
+}
+
+export interface TimeState {
+ /**
+ * The current time.
+ * @default Temporal.Now.zonedDateTimeISO()
+ * @readonly
+ * @type Temporal.ZonedDateTime
+ */
+ currentTime: Temporal.ZonedDateTime
+}
+
+export abstract class TimeCore {
+ store: Store
+ interval: NodeJS.Timeout | null = null
+ timeZone: Temporal.TimeZoneLike
+
+ constructor(options: TimeCoreOptions = {}) {
+ const defaultTimeZone = getDefaultTimeZone()
+ this.timeZone = options.timeZone || defaultTimeZone
+ this.store = new Store({
+ currentTime: Temporal.Now.zonedDateTimeISO(this.timeZone),
+ } as TState)
+ this.updateCurrentTime()
+ }
+
+ protected updateCurrentTime() {
+ this.store.setState((prev) => ({
+ ...prev,
+ currentTime: Temporal.Now.zonedDateTimeISO(this.timeZone),
+ }))
+ }
+
+ startUpdatingTime(intervalMs: number = 1000) {
+ if (!this.interval) {
+ this.interval = setInterval(() => this.updateCurrentTime(), intervalMs)
+ }
+ }
+
+ stopUpdatingTime() {
+ if (this.interval) {
+ clearInterval(this.interval)
+ this.interval = null
+ }
+ }
+
+ getCurrentTime(): Temporal.ZonedDateTime {
+ return this.store.state.currentTime
+ }
+}
diff --git a/packages/time/src/core/timer.ts b/packages/time/src/core/timer.ts
new file mode 100644
index 00000000..b1f72af7
--- /dev/null
+++ b/packages/time/src/core/timer.ts
@@ -0,0 +1,119 @@
+import { Store } from '@tanstack/store'
+import { TimeCore } from './time'
+import type { TimeCoreOptions, TimeState } from './time'
+
+export interface TimerOptions extends TimeCoreOptions {
+ /**
+ * The initial time for the timer.
+ */
+ initialTime: number
+ /**
+ * A callback that is called when the timer finishes.
+ */
+ onFinish?: () => void
+ /**
+ * A callback that is called when the timer finishes.
+ */
+ onStart?: () => void
+ /**
+ * A callback that is called when the timer stops.
+ */
+ onStop?: () => void
+ /**
+ * A callback that is called when the timer resets.
+ */
+ onReset?: () => void
+}
+
+interface TimerState extends TimeState {
+ /**
+ * The remaining time for the timer.
+ * @default 0
+ * @readonly
+ * @type number
+ */
+ remainingTime: number
+ /**
+ * Whether the timer is running.
+ * @default false
+ * @readonly
+ * @type boolean
+ */
+ isRunning: boolean
+}
+
+export interface TimerActions {
+ /**
+ * Start the timer.
+ */
+ start: () => void
+ /**
+ * Stop the timer.
+ */
+ stop: () => void
+ /**
+ * Reset the timer.
+ */
+ reset: () => void
+}
+
+export interface TimerApi extends TimerActions, TimerState {}
+
+export class Timer extends TimeCore implements TimerActions {
+ private options: TimerOptions
+
+ constructor(options: TimerOptions) {
+ super(options)
+ this.options = options
+ this.store = new Store({
+ remainingTime: options.initialTime,
+ isRunning: false,
+ currentTime: this.store.state.currentTime,
+ })
+ }
+
+ start() {
+ if (!this.store.state.isRunning) {
+ this.store.setState((prev) => ({
+ ...prev,
+ isRunning: true,
+ }))
+ this.startUpdatingTime(1000)
+ this.options.onStart?.()
+ }
+ }
+
+ stop() {
+ if (this.store.state.isRunning) {
+ this.store.setState((prev) => ({
+ ...prev,
+ isRunning: false,
+ }))
+ this.stopUpdatingTime()
+ this.options.onStop?.()
+ }
+ }
+
+ reset() {
+ this.stop()
+ this.store.setState((prev) => ({
+ ...prev,
+ remainingTime: this.options.initialTime,
+ }))
+ this.options.onReset?.()
+ }
+
+ protected updateCurrentTime() {
+ super.updateCurrentTime()
+ if (this.store.state.isRunning && this.store.state.remainingTime > 0) {
+ this.store.setState((prev) => ({
+ ...prev,
+ remainingTime: prev.remainingTime - 1,
+ }))
+ if (this.store.state.remainingTime <= 0) {
+ this.stop()
+ this.options.onFinish?.()
+ }
+ }
+ }
+}
diff --git a/packages/time/src/index.ts b/packages/time/src/index.ts
index 12981a25..3e0cfa01 100644
--- a/packages/time/src/index.ts
+++ b/packages/time/src/index.ts
@@ -1,4 +1,5 @@
/**
* TanStack Time
*/
-export * from './utils/parse';
\ No newline at end of file
+export * from './utils/parse'
+export * from './core'
diff --git a/packages/time/src/tests/isValidDate.test.ts b/packages/time/src/tests/isValidDate.test.ts
index 57a35567..01afdc69 100644
--- a/packages/time/src/tests/isValidDate.test.ts
+++ b/packages/time/src/tests/isValidDate.test.ts
@@ -4,7 +4,7 @@ import {isValidDate} from '../utils/isValidDate';
describe('isValidDate', () => {
test('should return true for a valid date', () => {
expect(isValidDate(new Date())).toBe(true);
- });
+ })
test('should return false for an invalid date', () => {
expect(isValidDate(new Date("invalid"))).toBe(false);
@@ -13,4 +13,4 @@ describe('isValidDate', () => {
test("should return false for null", () => {
expect(isValidDate(null)).toBe(false);
});
-});
\ No newline at end of file
+});
diff --git a/packages/time/src/tests/time.test.ts b/packages/time/src/tests/time.test.ts
new file mode 100644
index 00000000..29135d75
--- /dev/null
+++ b/packages/time/src/tests/time.test.ts
@@ -0,0 +1,64 @@
+import { Temporal } from '@js-temporal/polyfill'
+import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
+import { TimeCore } from '../core/time'
+
+
+export class TestTimeCore extends TimeCore {
+ getCurrentTime(): Temporal.ZonedDateTime {
+ return super.getCurrentTime()
+ }
+
+ startUpdatingTime(intervalMs: number = 1000) {
+ super.startUpdatingTime(intervalMs)
+ }
+
+ stopUpdatingTime() {
+ super.stopUpdatingTime()
+ }
+}
+
+describe('TimeCore', () => {
+beforeEach(() => {
+ vi.useFakeTimers()
+ const mockNow = Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1, hour: 0, minute: 0, second: 0 })
+ vi.setSystemTime(mockNow.toZonedDateTime('UTC').epochMilliseconds)
+ })
+
+ afterEach(() => {
+ vi.useRealTimers()
+ })
+
+ test('should initialize with the current time in the default time zone', () => {
+ const timeCore = new TestTimeCore()
+ const currentTime = Temporal.Now.zonedDateTimeISO()
+ expect(timeCore.getCurrentTime().toString()).toBe(currentTime.toString())
+ })
+
+ test('should initialize with the current time in the specified time zone', () => {
+ const timeZone = 'America/New_York'
+ const timeCore = new TestTimeCore({ timeZone })
+ const currentTime = Temporal.Now.zonedDateTimeISO(timeZone)
+ expect(timeCore.getCurrentTime().toString()).toBe(currentTime.toString())
+ })
+
+ test('should start updating the current time', () => {
+ const timeCore = new TestTimeCore()
+ timeCore.startUpdatingTime()
+ vi.advanceTimersByTime(1000)
+ const currentTime = Temporal.Now.zonedDateTimeISO()
+ expect(timeCore.getCurrentTime().epochMilliseconds).toBe(currentTime.epochMilliseconds)
+ })
+
+ test('should stop updating the current time', () => {
+ const timeCore = new TestTimeCore()
+ timeCore.startUpdatingTime()
+
+ vi.advanceTimersByTime(1000)
+ timeCore.stopUpdatingTime()
+ const stoppedTime = timeCore.getCurrentTime()
+
+ vi.advanceTimersByTime(1000)
+ const timeAfterStop = timeCore.getCurrentTime()
+ expect(timeAfterStop.epochMilliseconds).toBe(stoppedTime.epochMilliseconds)
+ })
+})
diff --git a/packages/time/src/tests/timer.test.ts b/packages/time/src/tests/timer.test.ts
new file mode 100644
index 00000000..4663c4a5
--- /dev/null
+++ b/packages/time/src/tests/timer.test.ts
@@ -0,0 +1,67 @@
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import { Timer } from '../core/timer';
+import type { TimerOptions } from '../core/timer';
+
+describe('Timer', () => {
+ let timer: Timer;
+ const onFinish = vi.fn();
+ const onStart = vi.fn();
+ const onStop = vi.fn();
+ const onReset = vi.fn();
+ const initialTime = 5;
+
+ beforeEach(() => {
+ vi.useFakeTimers();
+
+ const options: TimerOptions = {
+ initialTime,
+ onFinish,
+ onStart,
+ onStop,
+ onReset,
+ timeZone: 'America/New_York',
+ };
+
+ timer = new Timer(options);
+ });
+
+ it('should initialize with the correct remaining time and isRunning state', () => {
+ expect(timer.store.state.remainingTime).toBe(initialTime);
+ expect(timer.store.state.isRunning).toBe(false);
+ });
+
+ it('should start the timer', () => {
+ timer.start();
+ expect(timer.store.state.isRunning).toBe(true);
+ expect(onStart).toHaveBeenCalled();
+ });
+
+ it('should stop the timer', () => {
+ timer.start();
+ timer.stop();
+ expect(timer.store.state.isRunning).toBe(false);
+ expect(onStop).toHaveBeenCalled();
+ });
+
+ it('should reset the timer', () => {
+ timer.start();
+ timer.reset();
+ expect(timer.store.state.isRunning).toBe(false);
+ expect(timer.store.state.remainingTime).toBe(initialTime);
+ expect(onReset).toHaveBeenCalled();
+ });
+
+ it('should call onFinish when the timer reaches zero', () => {
+ timer.start();
+ vi.advanceTimersByTime(initialTime * 1000);
+ expect(timer.store.state.remainingTime).toBe(0);
+ expect(onFinish).toHaveBeenCalled();
+ expect(timer.store.state.isRunning).toBe(false);
+ });
+
+ it('should decrement the remaining time every second when running', () => {
+ timer.start();
+ vi.advanceTimersByTime(3000);
+ expect(timer.store.state.remainingTime).toBe(2);
+ });
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 37c8fc72..b206e85c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -150,6 +150,9 @@ importers:
packages/react-time:
dependencies:
+ '@tanstack/react-store':
+ specifier: ^0.5.2
+ version: 0.5.2(react-dom@18.2.0)(react@18.2.0)
'@tanstack/time':
specifier: workspace:*
version: link:../time
@@ -183,7 +186,18 @@ importers:
specifier: ^2.10.1
version: 2.10.1(@testing-library/jest-dom@6.4.2)(solid-js@1.7.8)(vite@5.2.6)
- packages/time: {}
+ packages/time:
+ dependencies:
+ '@js-temporal/polyfill':
+ specifier: ^0.4.4
+ version: 0.4.4
+ '@tanstack/store':
+ specifier: ^0.4.1
+ version: 0.4.1
+ devDependencies:
+ csstype:
+ specifier: ^3.1.3
+ version: 3.1.3
packages/vue-time:
dependencies:
@@ -541,7 +555,7 @@ packages:
dependencies:
'@ampproject/remapping': 2.3.0
'@babel/code-frame': 7.24.2
- '@babel/generator': 7.23.6
+ '@babel/generator': 7.24.1
'@babel/helper-compilation-targets': 7.23.6
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
'@babel/helpers': 7.24.1
@@ -2619,6 +2633,14 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
+ /@js-temporal/polyfill@0.4.4:
+ resolution: {integrity: sha512-2X6bvghJ/JAoZO52lbgyAPFj8uCflhTo2g7nkFzEQdXd/D8rEeD4HtmTEpmtGCva260fcd66YNXBOYdnmHqSOg==}
+ engines: {node: '>=12'}
+ dependencies:
+ jsbi: 4.3.0
+ tslib: 2.6.2
+ dev: false
+
/@leichtgewicht/ip-codec@2.0.5:
resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==}
dev: true
@@ -3288,6 +3310,26 @@ packages:
- vite
dev: true
+ /@tanstack/react-store@0.5.2(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-qzYy3ov/U/QZV8MX4zpStlp0Wwj91dmz7faVQwyLZLHiZp1VbOC5WfbUfbkKz9waL6MecZLKvosYVbgxz8Ty7Q==}
+ peerDependencies:
+ react: ^17.0.0 || ^18.0.0
+ react-dom: ^17.0.0 || ^18.0.0
+ dependencies:
+ '@tanstack/store': 0.5.2
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ use-sync-external-store: 1.2.0(react@18.2.0)
+ dev: false
+
+ /@tanstack/store@0.4.1:
+ resolution: {integrity: sha512-NvW3MomYSTzQK61AWdtWNIhWgszXFZDRgCNlvSDw/DBaoLqJIlZ0/gKLsditA8un/BGU1NR06+j0a/UNLgXA+Q==}
+ dev: false
+
+ /@tanstack/store@0.5.2:
+ resolution: {integrity: sha512-t3vR/nzKnixSmJcSjAULL4mlK6hApsC/pFNjwhLTgJJuWzGQaEjcaQvWfyD3LTVm4wljIL0gWW9cZ7Zrqb1bPQ==}
+ dev: false
+
/@testing-library/dom@9.3.4:
resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==}
engines: {node: '>=14'}
@@ -5113,7 +5155,7 @@ packages:
dom-serializer: 2.0.0
domhandler: 5.0.3
htmlparser2: 8.0.2
- postcss: 8.4.35
+ postcss: 8.4.38
postcss-media-query-parser: 0.2.3
dev: true
@@ -5151,12 +5193,12 @@ packages:
webpack:
optional: true
dependencies:
- icss-utils: 5.1.0(postcss@8.4.35)
- postcss: 8.4.35
- postcss-modules-extract-imports: 3.0.0(postcss@8.4.35)
- postcss-modules-local-by-default: 4.0.4(postcss@8.4.35)
- postcss-modules-scope: 3.1.1(postcss@8.4.35)
- postcss-modules-values: 4.0.0(postcss@8.4.35)
+ icss-utils: 5.1.0(postcss@8.4.38)
+ postcss: 8.4.38
+ postcss-modules-extract-imports: 3.0.0(postcss@8.4.38)
+ postcss-modules-local-by-default: 4.0.4(postcss@8.4.38)
+ postcss-modules-scope: 3.1.1(postcss@8.4.38)
+ postcss-modules-values: 4.0.0(postcss@8.4.38)
postcss-value-parser: 4.2.0
semver: 7.6.0
webpack: 5.90.3(esbuild@0.20.2)
@@ -7006,13 +7048,13 @@ packages:
safer-buffer: 2.1.2
dev: true
- /icss-utils@5.1.0(postcss@8.4.35):
+ /icss-utils@5.1.0(postcss@8.4.38):
resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
dependencies:
- postcss: 8.4.35
+ postcss: 8.4.38
dev: true
/identity-function@1.0.0:
@@ -7495,7 +7537,7 @@ packages:
resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
engines: {node: '>=8'}
dependencies:
- '@babel/core': 7.24.0
+ '@babel/core': 7.24.3
'@babel/parser': 7.24.1
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
@@ -7641,6 +7683,10 @@ packages:
argparse: 2.0.1
dev: true
+ /jsbi@4.3.0:
+ resolution: {integrity: sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==}
+ dev: false
+
/jsdom@24.0.0:
resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==}
engines: {node: '>=18'}
@@ -7891,8 +7937,6 @@ packages:
peerDependenciesMeta:
webpack:
optional: true
- webpack-sources:
- optional: true
dependencies:
webpack: 5.90.3(esbuild@0.20.2)
webpack-sources: 3.2.3
@@ -9113,45 +9157,45 @@ packages:
resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==}
dev: true
- /postcss-modules-extract-imports@3.0.0(postcss@8.4.35):
+ /postcss-modules-extract-imports@3.0.0(postcss@8.4.38):
resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
dependencies:
- postcss: 8.4.35
+ postcss: 8.4.38
dev: true
- /postcss-modules-local-by-default@4.0.4(postcss@8.4.35):
+ /postcss-modules-local-by-default@4.0.4(postcss@8.4.38):
resolution: {integrity: sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
dependencies:
- icss-utils: 5.1.0(postcss@8.4.35)
- postcss: 8.4.35
+ icss-utils: 5.1.0(postcss@8.4.38)
+ postcss: 8.4.38
postcss-selector-parser: 6.0.16
postcss-value-parser: 4.2.0
dev: true
- /postcss-modules-scope@3.1.1(postcss@8.4.35):
+ /postcss-modules-scope@3.1.1(postcss@8.4.38):
resolution: {integrity: sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
dependencies:
- postcss: 8.4.35
+ postcss: 8.4.38
postcss-selector-parser: 6.0.16
dev: true
- /postcss-modules-values@4.0.0(postcss@8.4.35):
+ /postcss-modules-values@4.0.0(postcss@8.4.38):
resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
dependencies:
- icss-utils: 5.1.0(postcss@8.4.35)
- postcss: 8.4.35
+ icss-utils: 5.1.0(postcss@8.4.38)
+ postcss: 8.4.38
dev: true
/postcss-selector-parser@6.0.16:
@@ -9458,7 +9502,7 @@ packages:
/regenerator-transform@0.15.2:
resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
dependencies:
- '@babel/runtime': 7.24.0
+ '@babel/runtime': 7.24.1
dev: true
/regex-parser@2.3.0:
@@ -9545,7 +9589,7 @@ packages:
adjust-sourcemap-loader: 4.0.0
convert-source-map: 1.9.0
loader-utils: 2.0.4
- postcss: 8.4.35
+ postcss: 8.4.38
source-map: 0.6.1
dev: true
@@ -10741,7 +10785,6 @@ packages:
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
- dev: true
/type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}