Giter Club home page Giter Club logo

strif's Introduction

๐Ÿ“‡ strif

NPM version Downloads Known Vulnerabilities

Utility for interpolating strings from a template with some data.


Features

  • โœ”๏ธŽ Simple
  • โœ”๏ธŽ Expandable/Configurable
  • โœ”๏ธŽ Type Checking
  • โœ”๏ธŽ No Dependencies

Introduction

Strif was initially created for one of my other libraries Loggin'JS which needed some features I could not find in other libraries and decided to do it myself.

What I needed was to be able to process a string in segments, and apply some format to each segment, with the option to enable/disable which parts are formatted and which parts are not.

For example:

  • Formating log messages, where some part need to be colored, some need to be converted to a specific date format. Etc...
  • Filling in a string with any data

Most simple example, so you get an idea:

const githubRepoLink = strif
  .template('https://github.com/{owner}/{repo}')
  .compile({ owner: 'loggin-js', repo: 'strif' });

console.log(githubRepoLink);

The above example would output the following:

Table Of Content

Installation

Install from npm:

$ npm install strif

Importing

With require:

const strif = require('strif');

With ES6 import:

import strif from 'strif';

In the browser:

<script src="node_modules/strif/dist/strif.dist.js"></script>

! NOTICE: Plugins currently don't work in browser, woking on it. PRs Welcome

Usage

Using in Node

Using strif is actually pretty easy, you can use the default formatter under strif. This formatter contains a set of predefined formatters (if you want to add you custom formatters, see the next point)

let template = strif.template('{time} {user} {message}');
template.compile(data);

// Or
strif.compile('{time} {user} {message}', data);

or create a custom one by using strif.create(opts), you can pass a set of transformers, plugins and other options

const formatter = strif.create({
  transformers: {
    date: s => new Date(s),
    lds:  d => d.toLocaleString()
  }
});

let template = formatter
  .template('{time} {user} {message}')
  .prop('time', { transformers: [`date`] });

let formatterString = 
  template.compile({
    time: 11223322,
    message: 'This is a super long message ',
    user: { name: 'Bob' }
  });

console.log(formatterString);

Using in Browser

Using strif in the browser is as simple as in node, just import the script strif/dist/strif.dist.js

<html lang="en">
  <head>
    <script src="node_modules/strif/dist/strif.dist.js"></script>
  </head>
  <body>
    <script>
      strif.create(); // strif is available
    </script>
  </body>
</html>

! NOTICE: Plugins currently don't work in browser, woking on it. PRs Welcome

Examples

I think looking at an example will help understand what strif does better than words:

Fill slug with data

const githubRepoLink = strif
  .template('https://github.com/{owner}/{repo}')
  .compile({ owner: 'loggin-js', repo: 'strif' });

console.log(githubRepoLink);

The above example would output the following:

Formatting a log message

const template = strif
  .template('[{time}] {user} - {message}', {
    props: {
      // `time` will be treated as a date, and apply the "lds" (toLocaleString) transformer
      time: { transformers: [`date`, `lds`] },

      // `user` specifies the dot notation path to the data ('user.name')
      // transformers can also be functions 
      user: { transformers: [(c) => c.toUpperCase()], accessor: 'user.name' },
    }
  })
  // props can be defined after creating the template, and can also define a type
  .prop('message', { type: 'string' });

// If we want to apply data to the template, we do it by using the `compile()` method
const logMessage = template.compile({
  time: Date.now(),
  user: { name: 'Manolo' },
  message: 'This is the message',
});

The above example would output the following:

Api

strif

Exported members from strif.

interface strif {
  create(opts: strif.StrifOptions): void;
  Formatter: strif.Formatter;
}

strif.Formatter

interface strif.Formatter {
  constructor(opts: strif.FormatterOptions);
  template(template: string, options: strif.TemplateOptions): strif.Template;
  fromFile(path: string, options: strif.TemplateOptions): strif.Template;
}

strif.Template

interface strif.Template {
  constructor(template: string, transformers: { [key: string]: (v) => v }, options: strif.TemplateOptions);
  prop(name: string, options: strif.PropOptions): this;
  print(): void;
  compile(data: object, options: { ignoreTransformers: string[] }): string;
}

strif.Prop

interface strif.Prop {
  constructor(name, opts: strif.PropOptions);
  getFromObject(obj: object): any;
}

strif.PropOptions

interface strif.PropOptions {
  accessor: string;
  type: string;
  transformers: string[];
}

strif.TemplateOptions

interface strif.TemplateOptions {
  props: strif.StrifProp[];
}

strif.FormatterOptions

interface strif.FormatterOptions {
  transformers: { [key: string]: (v) => v };
  plugins: string[]; 
}

Transformers

Transformers are functions that are used to process some segment of the template,
they will receive a value and they must also return a value, here are some example:

{
  transformers: {
    date: s => new Date(s),
    lds:  d => d.toLocaleString()
  }
}

Plugins

I added a little bit of plugin support, what a plugin actually is, is an object (for now) wich contains transformers (also for now), and will be attached to any template generated by that transformer. Here are some example:

const chalk = require('chalk');
module.exports = {
  transformers: {
    blue:  s => chalk.blue(s),
    gray:  s => chalk.gray(s),
    green: s => chalk.green(s),
  }
};

Should I use template literals instead?

Comparison between using strif and template literals.

For most usecases you will go fine just using template literals, but in some cases it is not enough, if that is your case, maybe strif can help out! Just keep reading a little bit :P

Process template (once)

Before:

const data = { user: { name: 'keff' }, messages: [...] };
const result = `${data.user.name} has ${data.message.length} messages';

After:

const data = { user: { name: 'keff' }, messages: [...] };
const result = strif.compile(
  '{user.name} has {messages.length} messages', 
  data,
);

If this is the only feature you need from this lib, better stick to template literals. As it does not offer much advantages over the native way. If you need more advanced templating, keep reading :)

Process template (repeatedly)

Before:

const format = (data) => `${data.user.name} has ${data.messageCount} messages`;
const data1 = { user: { name: 'keff' }, messageCount: 3 };
const data2 = { user: { name: 'bob' },  messageCount: 2 };

format(data1); // > keff has 3 messages
format(data2); // > bob has 2 messages

After:

const format = strif.template('{user.name} has {messageCount} messages');
const data1 = { user: { name: 'keff' }, messageCount: 3 };
const data2 = { user: { name: 'bob' },  messageCount: 2 };

format.compile(data1); // > keff has 3 messages
format.compile(data2); // > bob has 2 messages

This aproachs adds a bit more "usefullness" if you like, when creating a new reusabale template, you have the option to pass in a set of options, explained in the next example.

Process template, with transformations

In this example case, we want to capitalize the username optionally.

Before:

const format = (data, ignore = false) => {
  const name = data.user.name;
  const nameCapitalized = name[0].toUpperCase() + name.slice(1); 
  return `${ignore ? name : nameCapitalized} has ${data.messageCount} messages`;
};
const data1 = { user: { name: 'keff' }, messageCount: 3 };
const data2 = { user: { name: 'bob' },  messageCount: 2 };

format(data1);       // > Keff has 3 messages
format(data2, true); // > bob has 2 messages

After:
Strif offers a set of default transformers that you can apply to a specific value, which internally we call prop.

You can additionally create your own transformers, see this.

const format = strif
  .template('{name} has {messageCount} messages')
  .prop('name', { accessor: 'user.name', transformers: ['capitalize'] });

const data1 = { user: { name: 'keff' }, messageCount: 3 };
const data2 = { user: { name: 'bob' },  messageCount: 2 };

format.compile(data1); // > Keff has 3 messages
format.compile(
  data2, 
  { ignoreTransformers: ['capitalize'] }
); // > bob has 2 messages

See how the first example is actually quite messy to extend, both to add more transformers, and to disable them.

Found a bug or have a feature request

If you found a bug or have a feature request please dont hesitate on leaving a issue

Contributing

If you would like to collaborate please check CONTRIBUTING.md for more details.

Considerations

This project was in some way inspired by @davidchambers/string-format, at least in the sense of the transformers concept.

strif's People

Contributors

dependabot[bot] avatar nombrekeff avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

floki-gh

strif's Issues

Allow prop transformers to be functions

Transformers passed into template props, should also accept functions, not only by name.

const format = strif.template('...', {
  props: {
    name: {
      accessor: 'user.name',
      transformers: [
        'capitalize', 
        s => `[${s}]`, // like this
      ]
    }
  }
});

Dont require property to be defined in props

This should work fine, use prop name message as the accessor:

  formatter.template('[{time}] - {severity} - {message}', {
    props: {
      time: {
        transformers: ['date']
      },
      severity: {
        transformers: ['string']
      }
    }
  });

Now it requires to define the prop:

  formatter.template('[{time}] - {severity} - {message}', {
    props: {
      time: {
        transformers: ['date']
      },
      severity: {
        transformers: ['string']
      },
      message: { }
    }
  });

What if...

What if instead of doing this, if just one transformer is applied:

const format = strif.template('{name} has {messageCount} messages', {
  props: {
    name: {
      transformers: ['capitalize']
    }
  }
});

We could do something like:

const format = strif.template('{name | capitalize} has {messageCount} messages');

Global transformers

Add global transformers that apply to all templates generated by a formatter.

const formatter = strif.create({
  transformers: {
    trim: s => s.trim()
  },
  active: [  'trim'  ]
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.