From 5c62f2523e5780c9174e3f1fa82e89df06f7fa82 Mon Sep 17 00:00:00 2001
From: Lincoln <778157949@qq.com>
Date: Sat, 24 Aug 2024 08:36:12 +0000
Subject: [PATCH] ci
---
.eslintignore | 8 +--
.github/workflows/lint-and-type.yml | 31 +++++++++++
.github/workflows/test.yml | 27 ++++++++++
examples/demo-01/src/App.tsx | 72 ++++++++++++++------------
examples/demo-01/src/index.tsx | 10 ++--
package.json | 14 +++--
src/constants.ts | 6 +--
src/index.ts | 10 ++--
src/types.ts | 26 +++++-----
src/useAtomValueWithSchedule.ts | 80 ++++++++++++++---------------
src/useAtomWithSchedule.ts | 20 ++++----
src/useSetAtomWithSchedule.ts | 22 ++++----
src/utils.ts | 44 ++++++++--------
src/workLoop.ts | 46 ++++++++---------
14 files changed, 242 insertions(+), 174 deletions(-)
create mode 100644 .github/workflows/lint-and-type.yml
create mode 100644 .github/workflows/test.yml
diff --git a/.eslintignore b/.eslintignore
index ca9cd8a..63f6cdc 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,4 +1,4 @@
-/dist
-/src/vendor
-/pnpm-lock.yaml
-/jotai
\ No newline at end of file
+dist
+examples
+node_modules
+website
diff --git a/.github/workflows/lint-and-type.yml b/.github/workflows/lint-and-type.yml
new file mode 100644
index 0000000..eab3066
--- /dev/null
+++ b/.github/workflows/lint-and-type.yml
@@ -0,0 +1,31 @@
+name: lint and type
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ types: [opened, synchronize]
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 8
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18'
+ cache: pnpm
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile --prefer-offline
+ - name: Prettier
+ run: pnpm run prettier:ci
+ - name: Lint
+ run: pnpm run eslint:ci
+ - name: Type
+ run: pnpm run typecheck
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..5691705
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,27 @@
+name: test
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ types: [opened, synchronize]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 8
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18'
+ cache: pnpm
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile --prefer-offline
+ - name: Run Tests
+ run: pnpm run test
diff --git a/examples/demo-01/src/App.tsx b/examples/demo-01/src/App.tsx
index bca4cf4..bb8f9d4 100644
--- a/examples/demo-01/src/App.tsx
+++ b/examples/demo-01/src/App.tsx
@@ -1,65 +1,69 @@
-import { atom } from 'jotai';
-import { useAtomValueWithSchedule, useAtomWithSchedule, ImmediatePriority, LowPriority } from 'jotai-scheduler';
-import { useAtom, useAtomValue } from 'jotai';
+import { atom, useAtom, useAtomValue } from 'jotai'
+import {
+ useAtomValueWithSchedule,
+ useAtomWithSchedule,
+ ImmediatePriority,
+ LowPriority,
+} from 'jotai-scheduler'
-import './App.css';
+import './App.css'
-const anAtom = atom(0);
+const anAtom = atom(0)
const simulateHeavyRender = () => {
- const start = performance.now();
+ const start = performance.now()
while (performance.now() - start < 500) {}
-};
+}
-const Header = () => {
- simulateHeavyRender();
+function Header() {
+ simulateHeavyRender()
// const num = useAtomValue(anAtom);
- console.log('Header Component Render');
+ console.log('Header Component Render')
const num = useAtomValueWithSchedule(anAtom, {
priority: LowPriority,
- });
- return
Header-{num}
;
-};
+ })
+ return Header-{num}
+}
-const Footer = () => {
- simulateHeavyRender();
+function Footer() {
+ simulateHeavyRender()
// const num = useAtomValue(anAtom);
- console.log('Footer Component Render');
+ console.log('Footer Component Render')
const num = useAtomValueWithSchedule(anAtom, {
priority: LowPriority,
- });
- return Footer-{num}
;
-};
+ })
+ return Footer-{num}
+}
-const Sidebar = () => {
- simulateHeavyRender();
+function Sidebar() {
+ simulateHeavyRender()
// const num = useAtomValue(anAtom);
- console.log('Sidebar Component Render');
- const num = useAtomValueWithSchedule(anAtom);
- return Sidebar-{num}
;
-};
+ console.log('Sidebar Component Render')
+ const num = useAtomValueWithSchedule(anAtom)
+ return Sidebar-{num}
+}
-const Content = () => {
- simulateHeavyRender();
+function Content() {
+ simulateHeavyRender()
// const [num, setNum] = useAtom(anAtom);
- console.log('Content Component Render');
+ console.log('Content Component Render')
const [num, setNum] = useAtomWithSchedule(anAtom, {
priority: ImmediatePriority,
- });
+ })
return (
Content-{num}
- );
-};
+ )
+}
export function App() {
return (
@@ -71,5 +75,5 @@ export function App() {
- );
+ )
}
diff --git a/examples/demo-01/src/index.tsx b/examples/demo-01/src/index.tsx
index 8a2ac33..76f4a69 100644
--- a/examples/demo-01/src/index.tsx
+++ b/examples/demo-01/src/index.tsx
@@ -1,8 +1,8 @@
-import React from 'react';
-import { createRoot } from 'react-dom/client';
-import { App } from './App';
+import React from 'react'
+import { createRoot } from 'react-dom/client'
+import { App } from './App'
-const ele = document.getElementById('app');
+const ele = document.getElementById('app')
if (ele) {
- createRoot(ele).render(React.createElement(App));
+ createRoot(ele).render(React.createElement(App))
}
diff --git a/package.json b/package.json
index 5633e3e..2242334 100644
--- a/package.json
+++ b/package.json
@@ -27,10 +27,16 @@
"compile": "microbundle build -f modern,umd --globals react=React --no-compress",
"postcompile": "cp dist/index.modern.mjs dist/index.modern.js && cp dist/index.modern.mjs.map dist/index.modern.js.map",
"prepublishOnly": "pnpm run compile && pnpm run postcompile",
- "test": "run-s eslint tsc-test jest",
- "eslint": "eslint --ext .js,.ts,.tsx .",
- "jest": "jest",
- "tsc-test": "tsc --project . --noEmit"
+ "test": "jest --passWithNoTests",
+ "eslint": "eslint --no-eslintrc -c .eslintrc.json --fix '**/src/*.{js,jsx,ts,tsx}'",
+ "eslint:ci": "eslint --no-eslintrc -c .eslintrc.json '**/src/*.{js,jsx,ts,tsx}'",
+ "prettier": "prettier '**/{examples,src,__tests__,website}/**/*.{js,jsx,ts,tsx,md}' --write",
+ "prettier:ci": "prettier '**/{examples,src,__tests__,website}/**/*.{js,jsx,ts,tsx,md}' --list-different",
+ "typecheck": "tsc --project . --noEmit"
+ },
+ "prettier": {
+ "semi": false,
+ "singleQuote": true
},
"jest": {
"testEnvironment": "jsdom",
diff --git a/src/constants.ts b/src/constants.ts
index 9bbab58..8e2197e 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,3 +1,3 @@
-export const ImmediatePriority = 1;
-export const NormalPriority = 2;
-export const LowPriority = 3;
+export const ImmediatePriority = 1
+export const NormalPriority = 2
+export const LowPriority = 3
diff --git a/src/index.ts b/src/index.ts
index f37f996..93c8c02 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,5 @@
-export { useAtomValueWithSchedule } from './useAtomValueWithSchedule';
-export { useAtomWithSchedule } from './useAtomWithSchedule';
-export { useSetAtomWithSchedule } from './useSetAtomWithSchedule';
-export { LowPriority, NormalPriority, ImmediatePriority } from './constants';
-export * from './types';
+export { useAtomValueWithSchedule } from './useAtomValueWithSchedule'
+export { useAtomWithSchedule } from './useAtomWithSchedule'
+export { useSetAtomWithSchedule } from './useSetAtomWithSchedule'
+export { LowPriority, NormalPriority, ImmediatePriority } from './constants'
+export * from './types'
diff --git a/src/types.ts b/src/types.ts
index 0ecb13b..bc16dd1 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,25 +1,25 @@
-import { Atom, useStore } from 'jotai';
-import { ImmediatePriority, LowPriority, NormalPriority } from './constants';
+import { Atom, useStore } from 'jotai'
+import { ImmediatePriority, LowPriority, NormalPriority } from './constants'
-export type SetAtom = (...args: Args) => Result;
+export type SetAtom = (...args: Args) => Result
-export type AnyAtom = Atom;
+export type AnyAtom = Atom
-export type Store = ReturnType;
+export type Store = ReturnType
-export type Listener = () => void;
+export type Listener = () => void
export type PriorityLevel =
| typeof ImmediatePriority
| typeof NormalPriority
- | typeof LowPriority;
+ | typeof LowPriority
export type Options = Parameters[0] & {
- delay?: number;
- priority?: PriorityLevel;
-};
+ delay?: number
+ priority?: PriorityLevel
+}
export type Task = {
- priority: PriorityLevel;
- subscribe: Listener;
-};
+ priority: PriorityLevel
+ subscribe: Listener
+}
diff --git a/src/useAtomValueWithSchedule.ts b/src/useAtomValueWithSchedule.ts
index 82a2aac..c05960c 100644
--- a/src/useAtomValueWithSchedule.ts
+++ b/src/useAtomValueWithSchedule.ts
@@ -1,33 +1,33 @@
///
-import { useDebugValue, useEffect, useReducer } from 'react';
-import type { ReducerWithoutAction } from 'react';
-import type { Atom, ExtractAtomValue } from 'jotai';
-import { useStore } from 'jotai';
+import { useDebugValue, useEffect, useReducer } from 'react'
+import type { ReducerWithoutAction } from 'react'
+import type { Atom, ExtractAtomValue } from 'jotai'
+import { useStore } from 'jotai'
-import { NormalPriority } from './constants';
-import { isLastElement, isPromiseLike, use } from './utils';
-import { addTask, initiateWorkLoop } from './workLoop';
-import { AnyAtom, Options, Store, PriorityLevel, Listener } from './types';
+import { NormalPriority } from './constants'
+import { isLastElement, isPromiseLike, use } from './utils'
+import { addTask, initiateWorkLoop } from './workLoop'
+import { AnyAtom, Options, Store, PriorityLevel, Listener } from './types'
-const prioritySubscriptionsMap = new Map();
-const atomListenersMap = new Map>();
+const prioritySubscriptionsMap = new Map()
+const atomListenersMap = new Map>()
export function useAtomValueWithSchedule(
atom: Atom,
options?: Options,
-): Awaited;
+): Awaited
export function useAtomValueWithSchedule(
atom: AtomType,
options?: Options,
-): Awaited>;
+): Awaited>
export function useAtomValueWithSchedule(
atom: Atom,
options?: Options,
) {
- const store = useStore(options);
+ const store = useStore(options)
const [[valueFromReducer, storeFromReducer, atomFromReducer], rerender] =
useReducer<
@@ -35,43 +35,43 @@ export function useAtomValueWithSchedule(
undefined
>(
(prev) => {
- const nextValue = store.get(atom);
+ const nextValue = store.get(atom)
if (
Object.is(prev[0], nextValue) &&
prev[1] === store &&
prev[2] === atom
) {
- return prev;
+ return prev
}
- return [nextValue, store, atom];
+ return [nextValue, store, atom]
},
undefined,
() => [store.get(atom), store, atom],
- );
+ )
- let value = valueFromReducer;
+ let value = valueFromReducer
if (storeFromReducer !== store || atomFromReducer !== atom) {
- rerender();
- value = store.get(atom);
+ rerender()
+ value = store.get(atom)
}
- const delay = options?.delay;
- const priority = options?.priority ?? NormalPriority;
+ const delay = options?.delay
+ const priority = options?.priority ?? NormalPriority
useEffect(() => {
const subscribe = () => {
if (typeof delay === 'number') {
// delay rerendering to wait a promise possibly to resolve
- setTimeout(rerender, delay);
- return;
+ setTimeout(rerender, delay)
+ return
}
- rerender();
- };
+ rerender()
+ }
- prioritySubscriptionsMap.set(subscribe, priority);
- const listeners = atomListenersMap.get(atom) ?? new Set();
- listeners.add(subscribe);
- atomListenersMap.set(atom, listeners);
+ prioritySubscriptionsMap.set(subscribe, priority)
+ const listeners = atomListenersMap.get(atom) ?? new Set()
+ listeners.add(subscribe)
+ atomListenersMap.set(atom, listeners)
const unsub = store.sub(atom, () => {
if (isLastElement(listeners, subscribe)) {
@@ -79,22 +79,22 @@ export function useAtomValueWithSchedule(
addTask({
subscribe: listener,
priority: prioritySubscriptionsMap.get(listener)!,
- });
+ })
}
- initiateWorkLoop();
+ initiateWorkLoop()
}
- });
+ })
return () => {
- unsub();
- prioritySubscriptionsMap.delete(subscribe);
- listeners.delete(subscribe);
- };
- }, [atom, delay, priority, store]);
+ unsub()
+ prioritySubscriptionsMap.delete(subscribe)
+ listeners.delete(subscribe)
+ }
+ }, [atom, delay, priority, store])
- useDebugValue(value);
+ useDebugValue(value)
// TS doesn't allow using `use` always.
// The use of isPromiseLike is to be consistent with `use` type.
// `instanceof Promise` actually works fine in this case.
- return isPromiseLike(value) ? use(value) : (value as Awaited);
+ return isPromiseLike(value) ? use(value) : (value as Awaited)
}
diff --git a/src/useAtomWithSchedule.ts b/src/useAtomWithSchedule.ts
index b149c76..21bca27 100644
--- a/src/useAtomWithSchedule.ts
+++ b/src/useAtomWithSchedule.ts
@@ -6,25 +6,25 @@ import type {
PrimitiveAtom,
SetStateAction,
WritableAtom,
-} from 'jotai';
-import { useAtomValueWithSchedule } from './useAtomValueWithSchedule';
-import { useSetAtomWithSchedule } from './useSetAtomWithSchedule';
-import { Options, SetAtom } from './types';
+} from 'jotai'
+import { useAtomValueWithSchedule } from './useAtomValueWithSchedule'
+import { useSetAtomWithSchedule } from './useSetAtomWithSchedule'
+import { Options, SetAtom } from './types'
export function useAtomWithSchedule(
atom: WritableAtom,
options?: Options,
-): [Awaited, SetAtom];
+): [Awaited, SetAtom]
export function useAtomWithSchedule(
atom: PrimitiveAtom,
options?: Options,
-): [Awaited, SetAtom<[SetStateAction], void>];
+): [Awaited, SetAtom<[SetStateAction], void>]
export function useAtomWithSchedule(
atom: Atom,
options?: Options,
-): [Awaited, never];
+): [Awaited, never]
export function useAtomWithSchedule<
AtomType extends WritableAtom,
@@ -34,12 +34,12 @@ export function useAtomWithSchedule<
): [
Awaited>,
SetAtom, ExtractAtomResult>,
-];
+]
export function useAtomWithSchedule>(
atom: AtomType,
options?: Options,
-): [Awaited>, never];
+): [Awaited>, never]
export function useAtomWithSchedule(
atom: Atom | WritableAtom,
@@ -49,5 +49,5 @@ export function useAtomWithSchedule(
useAtomValueWithSchedule(atom, options),
// We do wrong type assertion here, which results in throwing an error.
useSetAtomWithSchedule(atom as WritableAtom, options),
- ];
+ ]
}
diff --git a/src/useSetAtomWithSchedule.ts b/src/useSetAtomWithSchedule.ts
index f7646ab..1d1f0d7 100644
--- a/src/useSetAtomWithSchedule.ts
+++ b/src/useSetAtomWithSchedule.ts
@@ -1,25 +1,25 @@
-import { useCallback } from 'react';
-import type { ExtractAtomArgs, ExtractAtomResult, WritableAtom } from 'jotai';
-import { useStore } from 'jotai';
-import { Options, SetAtom } from './types';
+import { useCallback } from 'react'
+import type { ExtractAtomArgs, ExtractAtomResult, WritableAtom } from 'jotai'
+import { useStore } from 'jotai'
+import { Options, SetAtom } from './types'
export function useSetAtomWithSchedule(
atom: WritableAtom,
options?: Options,
-): SetAtom;
+): SetAtom
export function useSetAtomWithSchedule<
AtomType extends WritableAtom,
>(
atom: AtomType,
options?: Options,
-): SetAtom, ExtractAtomResult>;
+): SetAtom, ExtractAtomResult>
export function useSetAtomWithSchedule(
atom: WritableAtom,
options?: Options,
) {
- const store = useStore(options);
+ const store = useStore(options)
const setAtom = useCallback(
(...args: Args) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -27,11 +27,11 @@ export function useSetAtomWithSchedule(
if (import.meta.env?.MODE !== 'production' && !('write' in atom)) {
// useAtom can pass non writable atom with wrong type assertion,
// so we should check here.
- throw new Error('not writable atom');
+ throw new Error('not writable atom')
}
- return store.set(atom, ...args);
+ return store.set(atom, ...args)
},
[store, atom],
- );
- return setAtom;
+ )
+ return setAtom
}
diff --git a/src/utils.ts b/src/utils.ts
index 4bc2aab..276696f 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,46 +1,46 @@
-import ReactExports from 'react';
+import ReactExports from 'react'
export function isLastElement(set: Set<() => void>, element: () => void) {
- const iterator = set.values();
- let result = iterator.next();
- let last = result.value;
+ const iterator = set.values()
+ let result = iterator.next()
+ let last = result.value
while (!result.done) {
- last = result.value;
- result = iterator.next();
+ last = result.value
+ result = iterator.next()
}
- return last === element;
+ return last === element
}
export const isPromiseLike = (x: unknown): x is PromiseLike =>
- typeof (x as any)?.then === 'function';
+ typeof (x as any)?.then === 'function'
export const use =
ReactExports.use ||
((
promise: PromiseLike & {
- status?: 'pending' | 'fulfilled' | 'rejected';
- value?: T;
- reason?: unknown;
+ status?: 'pending' | 'fulfilled' | 'rejected'
+ value?: T
+ reason?: unknown
},
): T => {
if (promise.status === 'pending') {
- throw promise;
+ throw promise
} else if (promise.status === 'fulfilled') {
- return promise.value as T;
+ return promise.value as T
} else if (promise.status === 'rejected') {
- throw promise.reason;
+ throw promise.reason
} else {
- promise.status = 'pending';
+ promise.status = 'pending'
promise.then(
(v) => {
- promise.status = 'fulfilled';
- promise.value = v;
+ promise.status = 'fulfilled'
+ promise.value = v
},
(e) => {
- promise.status = 'rejected';
- promise.reason = e;
+ promise.status = 'rejected'
+ promise.reason = e
},
- );
- throw promise;
+ )
+ throw promise
}
- });
+ })
diff --git a/src/workLoop.ts b/src/workLoop.ts
index 8991801..95584cc 100644
--- a/src/workLoop.ts
+++ b/src/workLoop.ts
@@ -1,48 +1,48 @@
-import { Task } from './types';
+import { Task } from './types'
-let isMessageLoopRunning: boolean = false;
+let isMessageLoopRunning: boolean = false
-const taskQueue: Array = [];
+const taskQueue: Array = []
function workLoop(): boolean {
- const highestPriority = Math.min(...taskQueue.map((task) => task.priority));
+ const highestPriority = Math.min(...taskQueue.map((task) => task.priority))
const highestPriorityList = taskQueue.filter(
(task) => task.priority === highestPriority,
- );
+ )
highestPriorityList.forEach((task) => {
- task.subscribe();
- taskQueue.splice(taskQueue.indexOf(task), 1);
- });
+ task.subscribe()
+ taskQueue.splice(taskQueue.indexOf(task), 1)
+ })
if (taskQueue.length > 0) {
- return true;
+ return true
}
- return false;
+ return false
}
function handleNextBatch() {
- const hasMoreWork = workLoop();
+ const hasMoreWork = workLoop()
if (hasMoreWork) {
- enqueueWorkExecution();
+ enqueueWorkExecution()
} else {
- isMessageLoopRunning = false;
+ isMessageLoopRunning = false
}
}
-const channel = new MessageChannel();
-const port = channel.port2;
-channel.port1.onmessage = handleNextBatch;
+const channel = new MessageChannel()
+const port = channel.port2
+channel.port1.onmessage = handleNextBatch
export const enqueueWorkExecution = () => {
- port.postMessage(null);
-};
+ port.postMessage(null)
+}
export const initiateWorkLoop = () => {
if (!isMessageLoopRunning) {
- isMessageLoopRunning = true;
- enqueueWorkExecution();
+ isMessageLoopRunning = true
+ enqueueWorkExecution()
}
-};
+}
export const addTask = (newTask: Task) => {
- taskQueue.push(newTask);
-};
+ taskQueue.push(newTask)
+}