npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@zhujianshi/fe

v0.0.4

Published

a way to bind environemnt to function

Downloads

6

Readme

@zhujianshi/fe

fefunction with environment的简写。通过在函数运行时附加的环境变量,可以拓展函数运行时的功能。

Usage

上下文(context)

const Context = createContext({ user: 'user' });
const testCase = fe(({ useContext }) => () => {
  return useContext(Context);
});
const times = Math.floor(Math.random() * 10);
const parent = fe(({ trigger, setContext }) => (i = 0): {
  user: string;
} => {
  setContext(Context, { user: 'simple user' });
  if (i === times) return trigger(testCase);
  return trigger(parent, ++i);
});
const value = run(fe(({ trigger }) => () => trigger(parent)));
expect(value.user).toBe('simple user');

同时支持async

const Context = createContext({ user: 'user' });
const testCase = fe(({ useContext }) => async () => {
  return useContext(Context).user;
});
const parent = fe(({ trigger, setContext }) => async () => {
  await Promise.resolve();
  setContext(Context, { user: 'parent' });
  return trigger(testCase);
});
const value = await run(
  fe(({ trigger, setContext }) => async () => {
    setContext(Context, { user: 'root' });
    return Promise.all([
      trigger(testCase),
      trigger(parent),
      trigger(testCase),
    ]);
  }),
);
expect(value).toEqual(['root', 'parent', 'root']);

缓存与ref

@zhujianshi/fe支持在多次调用的过程中根据依赖项缓存部分运算的结果。

const fn = jest.fn(() => Math.random());
const testCase = fe(({ useMemo }) => (...dependencies: any[]) => {
  const value = useMemo('memo', () => fn(), dependencies ?? []);
  return value;
});

const times = Math.floor(Math.random() * 100);
const value = run(
  fe(({ trigger }) => () => {
    return Array.from({ length: times })
      .map(() => trigger(testCase, 'key1', 'key2'))
      .reduce(
        (acc: null | number, v: number) => (acc === v ? acc : null) as any,
      );
  }),
);
expect(value).not.toBeNull();
expect(fn).toBeCalledTimes(1);

如果你对默认的缓存策略不满意,可以使用useRef来构建你自己的缓存行为。

const testCase = fe(({ useRef }) => () => {
  const ref = useRef('value', 0);
  ref.current++;
  return ref.current;
});
const countTimes = Math.floor(Math.random() * 100);

const value = run(
  fe(({ trigger }) => () => {
    return Array.from({ length: countTimes }).reduce(
      () => trigger(testCase),
      0,
    );
  }),
);
expect(value).toBe(countTimes);

⚠️需要注意的是,缓存行为依赖于关联环境的不变化(实际上,缓存的值存在环境中)。关于环境相关的解释请参考相关概念

Test

在测试环境下,@zhujianshi/fe通过劫持向fe节点注入的triggercall函数来达到单元测试中的mock能力。

mock 普通函数

在测试环境中,call(apiCall,...apiArgs)函数会先检索是否在全局范围内注册函数apiCall相关联的mock版本并优先返回mock版本。

setMock(true); // 开启测试环境

// src
const fn = () => 'no mock';
const testCase = fe(({ call }) => () => call(fn));
const main = fe(({ trigger }) => () => trigger(testCase));

// test
const f = jest.fn(() => 'mock');
mockFn(fn, f);
const value = run(main);
expect(value).toBe('mock');
expect(f).toBeCalledTimes(1);
// expect(fn).not.toBeCalled();

⚠️需要注意的是,当前版本使用function.name来作为查找函数的关键所在,没有function.name的匿名函数暂时没法使用此项功能。

mock fe

在测试环境中,trigger(serviceFe,...serviceArgs)函数会先检索是否在全局范围内注册feserviceFe相关联的mock版本并优先返回mock版本。

setMock(true); // 开启测试环境

// src
const fn = () => 'no mock';
const testCase = fe(({ call }) => () => call(fn));
const main = fe(({ trigger }) => () => trigger(testCase));

// test
const f = jest.fn(() => 'mock');
mockFe(testCase, f);
const value = run(main);
expect(value).toBe('mock');
expect(f).toBeCalledTimes(1);
// expect(fn).not.toBeCalled();

Api

fe

fe接受一个高阶方法,返回一个在环境中具有关联的fe节点。

type fe = <F extends Func>(f: FETypeImpl<F>)=>FEType<F>;
type FETypeImpl<F extends Func> = (
  handlers: {
    call: CallType;
    trigger: TriggerType;
    getEnvId: () => Key;
  } & Hooks,
) => F;

这个高阶函数接受一个包含api的对象并返回具体的方法,它的结构具体如下:

const feImpl = (feApis)=>(...args)=>{
  // your logic here
}

其中feApis包含

  • trigger
  • call
  • useRef
  • useMemo
  • useContext
  • setContext

run

run函数接受一个fe节点和调用参数,开启一次完成的调用过程并返回具体结果。

type RunType = <F extends Func>(fe: FEType<F>, ...args: Parameters<F>) => ReturnType<F>;

createContext

创建context。

type createContext = <T>(initialValue: T) => Context<T>;

以下4个api与测试相关

setMock && clearAllMock

setMock用于设置当前是否为mock环境,clearAllMock用于清除所有的mock.

type setMock = (flag:boolean) => void; // flag == true, 开启测试环境

type clearAllMock = () => void;

mockFn

用于mock某个被call调用的普通函数

type mockFn = <F extends Func>(f: F, mf: F) => void;

mockFe

用于mock某个被trigger调用的fe节点

type mockFe = <F extends Func>(fe: FEType<F>, mf: F) => void;

⚠️mockFe第二个参数是fe逻辑的具体实现,而不是一个用于产生fe节点的高阶函数。

以下的api均来源于高阶函数(feImpl)的feApis对象 以下的api均可适用于async(promise)环境

useContext && setContext

useContext用于从特定Context中取值,setContext用于设定特定Context的值用于子代fe存取.

type useContext = <T>(context: Context<T>) => T;
type setContext = <T>(context: Context<T>, value: T) => void; 

⚠️需要注意的是,在某个fe节点调用setContext仅仅会影响其后代节点,对自身取值并没有效果。

const Context = createContext(1);
const child = fe(({useContext})=>()=>{
  const value = useContext(Context); // value: 2;
});
const parent = fe(({useContext,setContext,trigger})=>()=>{
  const value = useContext(Context); // 读取的是上级的值, value:1
  trigger(child); // 由于在setContext之前,在第一运行的时候,并不清楚Context得到修改,故得到的是上级的旧值。
  setContext(Context,2);
  trigger(child);
  const value = useContext(Context); // 读取的是上级的值,value:1
});

同时需要注意的还有,在调用子fe时机,应该总在setContext逻辑之后,这样才能保证第一次调用过程中,子fe才可以识别到值。

useMemo && useRef

useRef和useMemo用于保证在多次调用逻辑时维持同一个值。

type useRef = <T>(key: string, value: T) => { current: T};
type useMemo = <F extends () => any>( key: string,f: F,dependencies?: any[])=>ReturnType<F>;

useMemodependenciesundefined时,默认缓存策略失效,每次都重新计算值。

Example

一个简易的使用redis和orm层进行查询的例子