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

@submodule/core

v7.6.0

Published

Structural way to build node and deno application

Downloads

2,771

Readme

Submodule documentation

The doc is out of date, soon to be updated

actualization in the document meant the result of a function excution, assume the function execution is expensive/or unexpected (for example, we expect limited amount of instances)

factory is the function that used to provide the value

Common usecases

Provide a value factory, actualize when needed. Use provide

const scope = createScope()

const valueExecutor = provide(() => /* Some value */)
const value = await scope.resolve(valueExecutor)

Factory can be a sync or async function. scope#resolve is always async ops Within same the same scope, the value is cached (singleton)

Derive value

const scope = createScope()

const seed = provide(async () => /* Read from config */)
const hashFnExecutor = map(seed, (seed) => () => /* Do hashing */)
const hashFn = await scope.resolve(hashFnExecutor)

Create submodule function that requires some input

Given this code

map(
  dependencies,                     // static dependency reference
  (dependencies) => {               // actualized static dependencies
                                    // preparation code, reusable part, cache etc
    return (inputParams: any[]): FinalValue => { // Runtime dependencies
      /* Implementation */
    }
  }
)

Example

const scope = createScope()

const seed = provide(async () => /* Read from config */)
const hashFnExecutor = map(seed, (seed) => (value: string) => /* Do hashing */)
const hashFn = await scope.resolve(hashFnExecutor)
// hashFn('value') <-- hashed value

Use multiple dependencies

Use combine to group multiple dependencies

const stringValue = provide(() => '1')
const intValue = provide(() => 2)
const combined: Executor<{ stringValue: string, intValue: number }> = combine({ stringValue, intValue })

const use = map(combined, ({ intValue, stringValue}) => { /**/ })
//                           ^^ int
//                                     ^^ string

// shortcut, works most of the time, soemtime typescript can't resolve it
const use = map({ stringValue, intValue }, ({ intValue, stringValue}) => { /**/ })

Group similar executors (like routes)

const stringValue1 = provide(() => '1')
const stringValue2 = provide(() => '2')

const stringValues: Executor<string[]> = group(stringValue1, stringValue2 )

Refer to the current scope inside a factory / conditional value

Use the special scoper to access to the current scope.

const constantSeed = provide(() => 1)
const randomSeed = provide(() => Math.random())

const seed = map(
  combine({
    scoper,
    constantSeed: value(constantSeed), // wrap inside value so it won't be resolved
    randomSeed: value(randomSeed), // wrap inside value so it won't be resolved
  }),
  async ({ 
    scoper,
    constantSeed,
    randomSeed
  }) => {
    if (condition) return await scoper.resolve(constantSeed)
    return await scoper.resolve(randomSeed)
  }
)

Can also use flat to resolve Executor<Executor<V>>

// In that case 
const seed = flat(map(
  combine({
    constantSeed: value(constantSeed), // wrap inside value so it won't be resolved
    randomSeed: value(randomSeed), // wrap inside value so it won't be resolved
  }),
  async ({ 
    constantSeed,
    randomSeed
  }) => condition ? constantSeed : randomSeed
))

And flatMap does exactly so

const seed = flatMap(
  combine({
    constantSeed: value(constantSeed), // wrap inside value so it won't be resolved
    randomSeed: value(randomSeed), // wrap inside value so it won't be resolved
  }),
  async ({ 
    constantSeed,
    randomSeed
  }) => condition ? constantSeed : randomSeed
)

Testing

Main purpose of submodule is to make the code

  • side-effect free (app'll be faster to start)
  • testing made easy
  • testing should be fast, and easy, mock free and can run in parallel

Scope is the key in this testing technique

There are certain common testing techniques

Assume value in the change to simluate different testing situations

For example

function tomorrow() {
  /** implementation */
}

This function is likely rely on new Date() to implement. This is a global object and by mocking the global object, the test will not be able to run in parallel and depending on test framework mocking to be able to test.

Rewrite the code into

const newDateFn = value(() => new Date())
const tomorrowFn = map(newDateFn, (newDateFn) => {
  /** Implementation **/
})

The implementation is mostly the same, now how to test?

// use ResolveValue to force value of dependency

const scope = createScope()
scope.resolveValue(newDateFn, value(() => /* mock date*/))

const r = await scope.resolve(torrowFn)
r() // <-- day after the mock date