x-stend / x

`x$` is a highly flexible and extendable utility library for JavaScript and TypeScript, designed to streamline the integration and extension of various utility libraries like lodash, moment, and ramda. With `x$`, developers can easily create custom higher-order functions, ensuring adaptability and maintainability in their codebase.
MIT License
0 stars 0 forks source link

[FEATURE]: Core x$ functionality #35

Open cstroliadavis opened 5 months ago

cstroliadavis commented 5 months ago

Feature Description

Create the initial core offering of x$

Problem and Motivation

x$ is nothing without a core offering. We need some initial code that will represent the core of x$. For now this will be a simple function with "minimal" functionality. It will be the default export of the library.

It should have the bare minimum to represent what x$ is intended to bring to the table.

Proposed Solution

x$ should initially have the following functionality:

$x will be a "function" with additional methods added that will allow it to act as a namespace for certain "static" methods that may persist information. The initial offering should have the following characteristics:

Alternatives Considered

n/a

Additional Context

At this time, there are still some areas of x$ that are unclear, but the general idea will be that it will have the following abilities in time:

Okay, a picture says a thousand words. Here are some examples of expected usage that might clarify eventual intent.

$x.initialize({
  canBeExtended: true, // default is true, so not really necessary to specify unless the answer is false
  limit: {
    namespaces:  [ // default is undefined. If an array exists, it can restrain what namespaces are allowed to extend x$
      'MyFunkyNamespace',
      /^MyFunky\w+$/,
      (namespace) => namespace.startsWith('MyFunky'),
    ],
   methods:  [ // similar to limit.namespaces, but will limit what methods or method names can be used
      '$string',
      '$date',
      /^\$[\w_0-9]+$/,
      (name) => name.startsWith('$') && !['$number', '$bigint'].includes(name),
    ],
  },
  plugins: [
    lodashX$(),
    ramdaX$(),
    {
      MyFunkyNamespace: {
        _dependencies: {
          'lodash-X$': {
            alias: '_',
            methods: ['deburr', 'words', { method: 'capitalize', as: 'sentenceCase' }]
          }
        },
        $string: {
          dependsOn: ['_'],
          methods: {
            prefix: (value, prefix) => `${prefix}${value}`,
            suffix: (value, suffix) => `${value}${suffix}`,
            titleCase: (value) => value.split(' ').map(deps.sentenceCase).join(' '),  // consider how to make dependencies available
            getWords: {
              fn: deps.words,
              returns: '$stringArray'
            },
          }
        },
        $stringArray: {
          dependsOn: ['_'],
          validation: {
            check: (value) => Array.isArray(value) && value.every((item) = typeof item === 'string'),
            failMessage: 'The value is either not an array or contains values that are not strings',
          },
          methods: {
            buff: (value) => value.map(deps.deburr),
          },
        }
      },
    }
  ],
});

Assuming we do not prevent it at the init level, we can pass some of these same values directly to x$ in the config param when we are using it to extend it further, or even remove functionality (for instance, the entire lodash and ramda methods are already available and perhaps we only want a few of those and/or perhaps we want to alias them as something else). The return value would be an object similar to the following:

    const { _, ramda} = x$();
    // OR
   const { _string, $rArray, $string } = x$(); // where  _string is from lodashX$ and $rArray is from ramdaX$ or something

   // then use it like
   $string(myValue).getWords().buff().valueOf; 

In the above, valueOf and toString are generally left alone and possibly even added to every value and will generally return the core resulting value, minus any extensions. If you add a valueOf or toString extension method, however, that will be used instead and will return the "returns" type you specify, if any, or the same type as the current extension (e.g. if you are using a method from $string that doesn't specify a return type, it will assume the return type is $string and will extend the result with that).

We're getting to the point that this may be confusing, so simply implement the minimum amount that seems reasonable, but keep in mind the bigger picture when doing so.