搜索

PythonReactSEOPDF
GitHub Skill

rimitive-module

Create Rimitive modules with defineModule. Use when adding new primitives, extending the signal system, creating adapters, or building composable functionality that integrates with compose().

hejhi☆ 0 仓库 Stars0 仓库 Forks

安装与使用

请根据来源仓库和你的 AI 工具说明安装此技能。先阅读技能文件和依赖要求,再添加到工作流。

查看来源 ↗使用指南

技能说明

Creating Rimitive Modules

Modules are the fundamental unit of composition in Rimitive. They declare dependencies and provide an implementation that becomes available on the composed service.

Rimitive's composition system isn't limited to the built-in primitives. You can define your own modules with defineModule and compose them alongside signals, view, or anything else.


The Pattern

import { defineModule } from '@rimitive/core';

const MyModule = defineModule({
  name: 'myFeature', // becomes svc.myFeature
  dependencies: [OtherModule], // optional, resolved by compose()
  create: ({ otherModule }) => {
    // receives resolved deps
    // Return the implementation
    return { /* API */ };
  },
});

The name becomes the key on the composed service. Whatever create returns becomes the value.


Basic Module

import { defineModule, compose } from '@rimitive/core';

const LoggerModule = defineModule({
  name: 'logger',
  create: () => ({
    log: (msg: string) => console.log(`[LOG] ${msg}`),
    error: (msg: string) => console.error(`[ERROR] ${msg}`),
  }),
});

const svc = compose(LoggerModule);
svc.logger.log('hello');

Modules with Dependencies

Dependencies are other modules. They're resolved automatically by compose().

import { defineModule, compose } from '@rimitive/core';
import { SignalModule, ComputedModule } from '@rimitive/signals/extend';

const CounterModule = defineModule({
  name: 'counter',
  dependencies: [SignalModule, ComputedModule],
  create: ({ signal, computed }) => (initial = 0) => {
    const count = signal(initial);
    const doubled = computed(() => count() * 2);
    return {
      count,
      doubled,
      increment: () => count(count() + 1),
      decrement: () => count(count() - 1),
    };
  },
});

const svc = compose(CounterModule);
const myCounter = svc.counter(10);

myCounter.count();      // 10
myCounter.increment();
myCounter.count();      // 11
myCounter.doubled();    // 22

Dependencies are resolved automatically. You only need to pass the modules you want—compose() includes transitive dependencies.


Module Lifecycle

Modules can hook into lifecycle events:

const ConnectionModule = defineModule({
  name: 'connection',
  create: () => {
    const ws = new WebSocket('wss://example.com');
    return {
      send: (msg: string) => ws.send(msg),
      socket: ws,
    };
  },
  init: (ctx) => {
    // Called before create, useful for setup
  },
  destroy: (ctx) => {
    // Called when svc.dispose() is invoked
  },
});

const svc = compose(ConnectionModule);
// ... use the connection
svc.dispose(); // triggers destroy hooks

Factory Modules (for Adapters)

When a module needs configuration, export a factory function:

import { defineModule } from '@rimitive/core';
import type { Adapter } from '@rimitive/view/types';

export const createElModule = (adapter: Adapter) =>
  defineModule({
    name: 'el',
    dependencies: [EffectModule],
    create: ({ effect }) => {
      // Use adapter and effect to build el()
      return (tag: string) => { /* ... */ };
    },
  });

// Usage:
const ElModule = createElModule(domAdapter);
const svc = compose(ElModule);

Beyond Reactivity

Here's the thing: @rimitive/core has no concept of reactivity. It's just a composition mechanism. You can use it for anything:

import { defineModule, compose } from '@rimitive/core';

const HttpModule = defineModule({
  name: 'http',
  create: () => ({
    get: (url: string) => fetch(url).then(r => r.json()),
    post: (url: string, data: unknown) =>
      fetch(url, { method: 'POST', body: JSON.stringify(data) }).then(r => r.json()),
  }),
});

const CacheModule = defineModule({
  name: 'cache',
  create: () => {
    const store = new Map<string, unknown>();
    return {
      get: <T>(key: string): T | undefined => store.get(key) as T,
      set: <T>(key: string, value: T): void => { store.set(key, value); },
      clear: () => store.clear(),
    };
  },
});

const ApiModule = defineModule({
  name: 'api',
  dependencies: [HttpModule, CacheModule],
  create: ({ http, cache }) => ({
    async getUser(id: string) {
      const cached = cache.get<User>(`user:${id}`);
      if (cached) return cached;

      const user = await http.get(`/api/users/${id}`);
      cache.set(`user:${id}`, user);
      return user;
    },
  }),
});

const svc = compose(ApiModule);
await svc.api.getUser('123');

This makes compose() useful for any library or application architecture—not just UI frameworks. You control what gets composed.


Tree-Shaking

Because you explicitly compose modules, everything is fully tree-shakeable. If you don't use EffectModule, it won't be in your bundle:

import { compose } from '@rimitive/core';
import { SignalModule, ComputedModule } from '@rimitive/signals/extend';

const svc = compose(SignalModule, ComputedModule);
// No effect or view code bundled

Instrumentation

For debugging/profiling support:

const SignalModule = defineModule({
  name: 'signal',
  dependencies: [GraphEdgesModule, SchedulerModule],
  create: ({ graphEdges, scheduler }) =>
    createSignalFactory({ graphEdges, scheduler }),
  instrument: (impl, instr, ctx) => (initialValue) => {
    const sig = impl(initialValue);
    instr.register(sig, 'signal', { initialValue });
    return sig;
  },
});

Export Types

Always export the implementation type for consumers:

export type Logger = {
  log: (msg: string) => void;
  error: (msg: string) => void;
};

export const LoggerModule = defineModule({
  name: 'logger',
  create: (): Logger => ({
    log: (msg) => console.log(`[LOG] ${msg}`),
    error: (msg) => console.error(`[ERROR] ${msg}`),
  }),
});

File Structure

packages/my-package/src/
├── index.ts          # Re-exports types
├── extend.ts         # Exports modules
├── my-feature.ts     # Module implementation
└── types.ts          # Type definitions

index.ts (types only):

export type { MyFeature, MyOptions } from './types';

extend.ts (modules):

export { MyFeatureModule } from './my-feature';

Naming Conventions

  • Module variable: PascalCaseModule (e.g., SignalModule, BatchModule)
  • Module name property: camelCase (e.g., 'signal', 'batch')
  • Factory function: createXModule (e.g., createElModule)

When to Create Modules

Create a module when you want to:

  • Add new primitives to the composed service
  • Share infrastructure (logging, http, storage) across behaviors
  • Encapsulate setup/teardown with lifecycle hooks
  • Build your own libraries on top of composition

For reusable reactive logic, prefer behaviors. Behaviors are simpler and don't require defineModule. Use modules when you need something that lives at the composition level.


Testing Modules

import { compose } from '@rimitive/core';
import { MyModule } from './my-module';

describe('MyModule', () => {
  it('provides the expected API', () => {
    const svc = compose(MyModule);

    expect(typeof svc.myFeature.doSomething).toBe('function');
  });

  it('integrates with dependencies', () => {
    const svc = compose(MyModule, OtherModule);

    svc.myFeature.doSomething();
    expect(svc.otherModule.wasNotified()).toBe(true);
  });
});

Common Module Types

  • Primitive modules: SignalModule, ComputedModule, EffectModule
  • Adapter-bound modules: createElModule(adapter), createMapModule(adapter)
  • Helper modules: BatchModule, UntrackModule, OnModule
  • Integration modules: MountModule, PortalModule

使用示例

查看来源仓库中的使用示例与配置说明。

查看来源 ↗

来源历史

收录时间
2026-01-20
索引更新时间
2026-01-26
Branch
main

收录与同步日期属于本站记录。源码修改历史请查看来源仓库。

GitHub ↗

chat Comments (0)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!