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

senia

v0.1.0

Published

A simple framework with imperative code to build declarative UI.

Downloads

1

Readme

eternal-senia

A simple framework with imperative code to build declarative UI.

usage

import { defineComponent, ref, render } from 'senia';
const Counter = defineComponent(_ => {
  const count = ref(0);
  const onclick = () => count.value++;
  return _ => <button onclick={onclick}>{count.value}</button>
});
render(<Counter />, document.querySelector('#app'));

api

render

function render(vnode: Vnode, node: Node): void;

render vnode as node's last child.

defineComponent

function createInstance<P extends {} = {}, C extends {} = {}>(init: P, ctx: C | null, doUpdate: () => void): {
  props: P;
  ctx: C & Record<PropertyKey, any>;
  on: <K extends keyof EventMap>(type: K, fn: (event: EventMap[K]) => any) => () => boolean;
  update: (fn?: () => any) => void;
  [symbol]: Record<keyof EventMap, Set<Function>>;
};

type Component<P extends {} = {}, C extends {} = {}> = (init: P, ins: ReturnType<typeof createInstance<P, C>>) => (props: P) => Vnode;

function defineComponent<P extends {} = any, C extends {} = any>(type: Component<P, C>): Component<P, C>;

defineComponent defines a autobind reactive state component.

const Counter = defineComponent((init, ins) => {
  const count = ref(0);
  const onclick = () => count.value++;
  return props => <button onclick={onclick}>{count.value}</button>
});
  • type: defineComponent(type), a function returns a render function, is the real component. Use imperative type function to build the declarative render function
    • init: init props
    • ins: component instance
      • ins.props: current props
      • ins.ctx: current component instance context with prototype linked to parent component instance context, ie: ins.ctx = Object.create(parentInstance.ctx), but you can never get parentInstance
      • ins.on: lifecycle events: instance.on('mount|mounted|update|updated|unmount|unmounted', fn), returns a revoke function.
      • ins.update: component refresh function
    • return a render function:
      • props: props in current render cycle. You can always use ins.props to get the newest props
  • defineComponent: autobind reactive state to component.

    In fact, you can define a component with only type, but reactive state, ie ref or computed, will not autobind to it. defineComponent is just a way to bind component's ins.update to reactive state .

ref

type Ref<T> = { value: T; };
function ref<T>(): Ref<T | undefined>;
function ref<T>(value: T): Ref<T>;

ref defines a reactive state. Unlike vue-composition-api/ref, ref in senia is just shallow reactive.

reactive state can only be autobind with defineComponent.

computed

function computed<T>(fn: () => T): {
  readonly value: T;
  force(): T;
};

watch

watch reactive state and do effect when state change.

const count = ref(0);
watch(count, (cv, pv) => { xxx }, { immediate: false, flush: 'post' });
  • immediate: run the effect function immediately, default to false
  • flush:
    • post: run the effect function after a macro task, default to post
    • pre: run the effect function after a micro task
    • sync: run the effect function immediate after reactive state changed

concepts in qa style

Q: What's a state?
A: let a = 0 or var a = 0.

Q: A variable?
A: Yes, so const a = 0 isn't a state.

Q: How to change the state?
A: Change the variable and if needed, tell those components which depend on the variable to update themselves.

Q: So you write instance.update(() => count++)?
A: Yes, and count++; instance.update() is right too. instance.update(fn) is just to make fn async.

Q: What's a reactive state?
A: It's just a state with getter, setter and listeners.

Q: So senia use const count = ref(0) to define a reactive state?
A: Yes. And reactive state can only be used in components defined with defineComponent. defineComponent is a function to bind instance.update to reactive state's listeners.

Q: How to define Context?
A: Just instance.ctx.abc = xyz and tell the component to instance.update. In fact it's instance.ctx = Object.create(parentComponentInstance.ctx) internally, changes to parentComponentInstance.ctx will be accessed in instance.ctx.

There is no way to get parentComponentInstance.ctx except Object.getPrototypeOf(instance.ctx), but don't do that.

Q: Then, lifecycle?
A: Just instance.on('mount|mounted|update|updated|unmount|unmounted', fn). You can have multiple hooks in one lifecycle: instance.on('mounted', hook_one); some_other_things(); instance.on('mounted', hook_two()).